URL
https://opencores.org/ocsvn/cpu16/cpu16/trunk
Subversion Repositories cpu16
[/] [cpu16/] [trunk/] [cpu16.v] - Rev 3
Compare with Previous | Blame | View Log
/* * None pipelined, 3-state, 16-bit CPU * * Copyright (C) 2019, Yvo Zoer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // comment this line out to use LE's (warning: size goes up dramatically to around 800LE's) `define USE_RAM_FOR_REGFILE module cpu16 ( clk, reset_n, clk_en, address, din, dout, cs_n, oe_n, we_n, ub_n, lb_n, irq, // combined interrupt request from intc busreq, busack ); input clk; input reset_n; output [15:0] address; input clk_en; input [15:0] din; output [15:0] dout; output cs_n; output oe_n; output we_n; output ub_n; output lb_n; input irq; input busreq; output busack; // alu parameter OP_SUB = 5'd0; // ok parameter OP_SBC = 5'd1; // ok parameter OP_ADD = 5'd2; parameter OP_ADC = 5'd3; parameter OP_CMP = 5'd4; parameter OP_AND = 5'd5; // ok parameter OP_OR = 5'd6; // ok parameter OP_XOR = 5'd7; // ok // alu immediate parameter OP_SUBI = 5'd8; // ok parameter OP_SBCI = 5'd9; // ok parameter OP_ADDI = 5'd10; // ok parameter OP_ADCI = 5'd11; // ok parameter OP_CMPI = 5'd12; parameter OP_ANDI = 5'd13; // ok parameter OP_ORI = 5'd14; // ok parameter OP_XORI = 5'd15; // ok // shifter + load parameter OP_MOV = 5'd16; parameter OP_SRAV = 5'd17; parameter OP_SLLV = 5'd18; parameter OP_LD = 5'd19; // shifter immediate + store parameter OP_LUI = 5'd20; parameter OP_SRA = 5'd21; parameter OP_SLL = 5'd22; parameter OP_ST = 5'd23; // jump parameter OP_J = 5'd24; parameter OP_JAL = 5'd25; parameter OP_JR = 5'd26; parameter OP_JALR = 5'd27; // conditional branch parameter OP_BXX = 5'd28; parameter OP_MSR = 5'd29; parameter OP_RFE = 5'd30; parameter OP_SWI = 5'd31; // states parameter STATE_FETCH = 4'h0; parameter STATE_DECODE = 4'h1; parameter STATE_ALU = 4'h2; parameter STATE_SHIFT = 4'h3; parameter STATE_LOAD = 4'h4; parameter STATE_STORE = 4'h5; parameter STATE_J = 4'h6; parameter STATE_JAL = 4'h7; parameter STATE_BRANCH = 4'h8; parameter STATE_MOVSR = 4'h9; parameter STATE_RFE = 4'ha; parameter STATE_SWI = 4'hb; parameter STATE_INTREQ = 4'hc; parameter STATE_BUSREQ = 4'hd; // instruction register wires wire [4:0] ir_opcode = ir[15:11]; wire [2:0] ir_dst = ir[10:8]; wire [2:0] ir_src = ir[7:5]; wire ir_size = ir[4]; // byte/word flag wire [3:0] ir_imm4 = ir[3:0]; // load/store offset wire [7:0] ir_imm8 = ir[7:0]; // immediate value / relative conditional branch wire [10:0] ir_imm11 = ir[10:0]; // unconditional branch // busack valid once we're in the busreq state reg busack; always @(*) if ( state == STATE_BUSREQ ) busack = 1'b1; else busack = 1'b0; // chip-select reg cs_n; always @(*) case (state) STATE_FETCH, STATE_LOAD, STATE_STORE : cs_n = 1'b0; default : cs_n = 1'b1; endcase // output enable reg oe_n; always @(*) case (state) STATE_FETCH, STATE_LOAD : oe_n = 1'b0; default : oe_n = 1'b1; endcase // memory write signal reg we_n; always @(posedge clk) if ( state == STATE_STORE ) we_n <= clk_en; else we_n <= 1'b1; // upper / lower byte write/read enable // NOTE: could integrate dout here as well.. reg ub_n; reg lb_n; always @(*) if ( state == STATE_STORE ) begin if ( ir_size ) begin // word ub_n = 1'b0; lb_n = 1'b0; end else begin // byte if ( address[0] ) begin ub_n = 1'b1; lb_n = 1'b0; end else begin ub_n = 1'b0; lb_n = 1'b1; end end end else begin // word access for everything else ub_n = 1'b0; lb_n = 1'b0; end // data output logic reg [15:0] dout; always @(*) if ( ir_size ) dout = qa; // word else begin if ( address[0] ) dout = { 8'h00, qa[7:0] }; else dout = { qa[7:0], 8'h00 }; end // address mux reg [15:0] address; always @(*) case (state) STATE_LOAD, STATE_STORE : address = qb + ir_imm4; default : address = pc; // { pc[15:1], 1'b0 }; endcase // main state machine reg [3:0] state, next_state; always @(*) case (state) STATE_FETCH : next_state = STATE_DECODE; STATE_DECODE : begin casex (ir_opcode) 5'b0xxxx : next_state = STATE_ALU; // sub/sbc/add/adc/and/or/xor/cmp 5'b10x0x, 5'b10x10 : next_state = STATE_SHIFT; 5'b10011 : next_state = STATE_LOAD; 5'b10111 : next_state = STATE_STORE; 5'b110x0 : next_state = STATE_J; // 24/26 5'b110x1 : next_state = STATE_JAL; // 25/27 5'b11100 : next_state = STATE_BRANCH; 5'b11101 : next_state = STATE_MOVSR; 5'b11110 : next_state = STATE_RFE; 5'b11111 : next_state = STATE_SWI; default : next_state = STATE_FETCH; endcase end STATE_INTREQ : next_state = STATE_FETCH; STATE_BUSREQ : begin if ( busreq ) next_state = STATE_BUSREQ; else next_state = STATE_FETCH; // will miss interrupt when coming out of busreq (STATE_NOP?) end default : begin if ( busreq ) next_state = STATE_BUSREQ; else if ( irq & i ) // only fires when interrupt enable is set (i = 1) next_state = STATE_INTREQ; else next_state = STATE_FETCH; end endcase // instruction register reg [15:0] ir, next_ir; always @(*) if ( state == STATE_FETCH ) next_ir = din; else next_ir = ir; // exception pc- and status register reg [15:0] epc, next_epc; reg [4:0] esr, next_esr; always @(*) case (state) STATE_INTREQ : begin next_epc = pc; next_esr = { i,n,v,z,c }; end default : begin next_epc = epc; next_esr = esr; end endcase // source data for program counter ( register / immediate ) reg [15:0] pc_data; always @(*) if ( ir_opcode[1] ) // immediate for PC pc_data = qb; //{ qb[15:1], 1'b0 }; // TEST: jr/jalr else pc_data = pc + { { 4 { ir_imm11[10] } }, ir_imm11[10:0], 1'b0 }; // TEST: j/jal doubled for jumps // condition check reg condition_true; always @(*) case (ir_dst) 3'd0 : condition_true = z; 3'd1 : condition_true = ~z; 3'd2 : condition_true = ~n; 3'd3 : condition_true = n; 3'd4 : condition_true = ~c; 3'd5 : condition_true = c; 3'd6 : condition_true = ~v; 3'd7 : condition_true = v; endcase // program counter reg [15:0] pc, next_pc; always @(*) case (state) STATE_FETCH : next_pc = pc + 16'd2; STATE_SWI : next_pc = { ir_imm8, 1'b0 }; STATE_J, STATE_JAL : next_pc = pc_data; STATE_BRANCH : begin if ( condition_true ) next_pc = pc + { { 7 { ir_imm8[7] } }, ir_imm8[7:0], 1'b0 }; else next_pc = pc; end STATE_INTREQ : next_pc = 16'h0002; // interrupt vector appears at 0x0002 STATE_RFE : next_pc = epc; default : next_pc = pc; endcase // alu data mux reg [15:0] alu_data; always @(*) if (ir_opcode[3]) // immediate alu_data = { 8'h00, ir_imm8 }; else alu_data = qb; // register // alu wire [15:0] alu_result; wire alu_n; wire alu_v; wire alu_z; wire alu_c; alu i_alu ( .a(qa), .b(alu_data), .c_in(c), .func(ir_opcode[2:0]), .result(alu_result), .n(alu_n), .v(alu_v), .z(alu_z), .c(alu_c) ); // shifter data mux reg [15:0] shifter_data; always @(*) if ( ir_opcode[2] ) // immediate shifter_data = { ir_imm8, 8'h00 }; // lui from instruction word else shifter_data = qb; // mov // shifter distance -- roll into b argument inside shifter? reg [3:0] shift_distance; always @(*) if ( ir_opcode[2] ) shift_distance = ir_imm4; else shift_distance = qb[3:0]; // shifter wire [15:0] shifter_result; wire shift_n; wire shift_z; wire shift_c; wire shift_v; shifter i_shifter ( .a(qa), .b(shifter_data), .n_in(n), .v_in(v), .z_in(z), .c_in(c), .distance(shift_distance), .func(ir_opcode[1:0]), .result(shifter_result), .n(shift_n), .v(shift_v), .z(shift_z), .c(shift_c) ); // memory data mux for lw/lb reg [15:0] mem_data; always @(*) if ( ir_size ) mem_data = din; else begin if ( address[0] ) mem_data = { 8'h00, din[7:0] }; else mem_data = { 8'h00, din[15:8] }; end // register data reg [15:0] reg_data; always @(*) case (state) STATE_ALU : reg_data = alu_result; STATE_SHIFT : reg_data = shifter_result; STATE_JAL, STATE_SWI : reg_data = pc; default : reg_data = mem_data; endcase // register writeback reg reg_write; always @(*) case (state) STATE_ALU, STATE_SHIFT, STATE_LOAD, STATE_JAL, STATE_SWI : reg_write = 1'b1; default : reg_write = 1'b0; endcase // override dst address for JAL to save 3 bits reg [2:0] ra_dst; always @(*) if ( state == STATE_JAL ) ra_dst = 3'd6; // hard linked register else ra_dst = ir_dst; // register file `ifdef USE_RAM_FOR_REGFILE wire [15:0] qa, qb; regfile8x16 i_regfile ( .clock(clk), .data(reg_data), .rdaddress_a(ir_dst), .rdaddress_b(ir_src), .wraddress(ra_dst), .wren(reg_write&clk_en), .qa(qa), .qb(qb) ); `else reg [15:0] regs[7:0]; always @(posedge clk) if (reg_write&clk_en) regs[ir_dst] <= reg_data; wire [15:0] qa = regs[ir_dst]; wire [15:0] qb = regs[ir_src]; `endif // flags reg i, next_i; reg n, next_n; reg v, next_v; reg z, next_z; reg c, next_c; always @(*) case (state) STATE_ALU : begin next_i = i; next_n = alu_n; next_v = alu_v; next_z = alu_z; next_c = alu_c; end STATE_SHIFT : begin next_i = i; next_n = shift_n; next_v = shift_v; next_z = shift_z; next_c = shift_c; end STATE_MOVSR : begin next_i = (i & ~ir_imm8[4]) | ( ir_imm8[7] & ir_imm8[4]); next_n = (n & ~ir_imm8[3]) | ( ir_imm8[7] & ir_imm8[3]); next_v = (v & ~ir_imm8[2]) | ( ir_imm8[7] & ir_imm8[2]); next_z = (z & ~ir_imm8[1]) | ( ir_imm8[7] & ir_imm8[1]); next_c = (c & ~ir_imm8[0]) | ( ir_imm8[7] & ir_imm8[0]); end STATE_INTREQ : begin next_i = 1'b0; // disable interrupts next_n = n; next_v = v; next_z = z; next_c = c; end STATE_RFE : begin next_i = esr[4]; next_n = esr[3]; next_v = esr[2]; next_z = esr[1]; next_c = esr[0]; end default : begin next_i = i; next_n = n; next_v = v; next_z = z; next_c = c; end endcase // synchronous writeback always @(posedge clk or negedge reset_n) if (!reset_n) begin state <= STATE_FETCH; ir <= 16'd0; pc <= 16'd0; i <= 1'b1; // TEST: enable interrupts n <= 1'b0; v <= 1'b0; z <= 1'b0; c <= 1'b0; epc <= 16'd0; esr <= 5'd0; end else if ( clk_en ) begin state <= next_state; ir <= next_ir; pc <= next_pc; i <= next_i; n <= next_n; v <= next_v; z <= next_z; c <= next_c; epc <= next_epc; esr <= next_esr; end endmodule