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