factorio-riscv/board/parts/hart.sv

340 lines
12 KiB
Systemverilog
Raw Normal View History

2023-12-29 18:45:02 +00:00
`define INS_LUI 7'b0110111
`define INS_AUIPC 7'b0010111
`define INS_JAL 7'b1101111
`define INS_JALR 7'b1100111
`define INS_BRANCH 7'b1100011
`define INS_LOAD 7'b0000011
`define INS_STORE 7'b0100011
`define INS_IMMOP 7'b0010011
`define INS_REGOP 7'b0110011
`define INS_FENCE 7'b0001111
`define INS_ENVOP 7'b1110011
`define INS_BRANCH_EQ 3'b000
`define INS_BRANCH_NE 3'b001
`define INS_BRANCH_LT 3'b100
`define INS_BRANCH_GE 3'b101
`define INS_BRANCH_LTU 3'b110
`define INS_BRANCH_GEU 3'b111
`define INS_LOAD_8 3'b000
`define INS_LOAD_16 3'b001
`define INS_LOAD_32 3'b010
`define INS_LOAD_U8 3'b100
`define INS_LOAD_U16 3'b101
`define INS_STORE_8 3'b000
`define INS_STORE_16 3'b001
`define INS_STORE_32 3'b010
`define INS_ALUOP_ADD 3'b000
`define INS_ALUOP_SLT 3'b010
`define INS_ALUOP_SLTU 3'b011
`define INS_ALUOP_XOR 3'b100
`define INS_ALUOP_OR 3'b110
`define INS_ALUOP_AND 3'b111
`define INS_ALUOP_SLL 3'b001
`define INS_ALUOP_SRL 3'b101
`define INS_ENVOP_CALL 12'b000000000000
`define INS_ENVOP_BREAK 12'b000000000001
2023-12-29 20:00:02 +00:00
`define F3_DONT_CARE 3'b???
`define F7_DONT_CARE 7'b???????
2023-12-29 14:10:10 +00:00
// RISC-V CPU Hart
module hart(
clk,
pmmu_rd, pmmu_wr,
pmmu_addr, pmmu_datar, pmmu_dataw
);
input clk; // the clock. pretty important.
output reg pmmu_rd;
output reg pmmu_wr;
output reg [31:0] pmmu_addr;
input reg [31:0] pmmu_datar;
output reg [31:0] pmmu_dataw;
reg [31:0] reg_pc = 0;
2023-12-29 18:45:02 +00:00
reg [31:0] reg_int [31:0];
2023-12-29 14:10:10 +00:00
2023-12-29 18:45:02 +00:00
// FETCH DATA
reg [31:0] t_ins = 0; // temp for being processed instruction
// DECODE DATA
reg [6:0] t_d_op;
reg [4:0] t_d_rd;
reg [2:0] t_d_f3;
reg [4:0] t_d_r1;
reg [4:0] t_d_r2;
reg [6:0] t_d_f7;
reg [31:0] t_d_immi;
reg [31:0] t_d_imms;
reg [31:0] t_d_immb;
reg [31:0] t_d_immu;
reg [31:0] t_d_immj;
reg [31:0] t_d_imm;
// GET REGISTERS
reg [31:0] t_r_a;
reg [31:0] t_r_b;
// EXECUTE INSTRUCTION
reg [31:0] t_e_result;
reg t_e_branch;
reg [31:0] t_e_address;
2023-12-29 21:59:32 +00:00
reg [31:0] t_e_tmp1;
2023-12-29 14:10:10 +00:00
always begin
// instruction fetch
@(posedge clk);
$display("== INSTRUCTION FETCH (0x%08h) ==", reg_pc);
pmmu_addr = reg_pc;
pmmu_rd = 1;
// instruction decode
@(posedge clk);
$display("== INSTRUCTION DECODE (0x%08h) ==", pmmu_datar);
t_ins = pmmu_datar;
pmmu_addr = 0;
2023-12-29 18:45:02 +00:00
pmmu_rd = 0;
t_d_op = t_ins[6:0];
t_d_rd = t_ins[11:7];
t_d_f3 = t_ins[14:12];
t_d_r1 = t_ins[19:15];
t_d_r2 = t_ins[24:20];
t_d_f7 = t_ins[31:25];
2023-12-29 14:10:10 +00:00
2023-12-29 18:45:02 +00:00
t_d_immi = {{21{t_ins[31]}}, t_ins[30:20] };
t_d_imms = {{21{t_ins[31]}}, t_ins[30:25], t_ins[11:7] };
t_d_immb = {{20{t_ins[31]}}, t_ins[7], t_ins[30:25], t_ins[11:8], 1'b0 };
t_d_immu = { t_ins[31:12], 12'b0 };
t_d_immj = {{12{t_ins[31]}}, t_ins[19:12], t_ins[20], t_ins[30:21], 1'b0 };
case (t_d_op)
`INS_JALR,
`INS_LOAD,
`INS_IMMOP: t_d_imm = t_d_immi;
`INS_JAL: t_d_imm = t_d_immj;
`INS_STORE: t_d_imm = t_d_imms;
`INS_BRANCH: t_d_imm = t_d_immb;
`INS_LUI,
`INS_AUIPC: t_d_imm = t_d_immu;
`INS_ENVOP,
`INS_FENCE,
`INS_REGOP: t_d_imm = 0;
2023-12-29 20:00:02 +00:00
default: $error("INVALID INSTRUCTION %b", t_d_op);
2023-12-29 18:45:02 +00:00
endcase
$display("INS: %7b %3b %7b %d %d -> %d", t_d_op, t_d_f3, t_d_f7, t_d_r1, t_d_r2, t_d_rd);
$display("IMM: U[%d] S[%d]", t_d_imm, $signed(t_d_imm));
// get registers
2023-12-29 14:10:10 +00:00
@(posedge clk);
$display("== GET REGISTERS ==");
2023-12-29 18:45:02 +00:00
t_r_a = reg_int[t_d_r1];
t_r_b = reg_int[t_d_r2];
$display("REG: %d[%d] %d[%d]", t_d_r1, t_r_a, t_d_r2, t_r_b);
2023-12-29 14:10:10 +00:00
// execute
@(posedge clk);
$display("== EXECUTE ==");
2023-12-29 20:00:02 +00:00
t_e_address = 0;
t_e_branch = 0;
t_e_result = 0;
casez ({ t_d_op, t_d_f3, t_d_f7 })
{ `INS_LUI, `F3_DONT_CARE, `F7_DONT_CARE }: t_e_result = t_d_imm;
{ `INS_AUIPC, `F3_DONT_CARE, `F7_DONT_CARE }: t_e_result = t_d_imm + reg_pc;
{ `INS_JAL, `F3_DONT_CARE, `F7_DONT_CARE }: begin
t_e_result = reg_pc + 4;
t_e_address = reg_pc + t_d_imm;
t_e_branch = 1;
end
{ `INS_JALR, `F3_DONT_CARE, `F7_DONT_CARE }: begin
t_e_result = reg_pc + 4;
t_e_address = (t_r_a + t_d_imm) & ~1;
t_e_branch = 1;
end
{ `INS_BRANCH, `INS_BRANCH_EQ, `F7_DONT_CARE }: begin
if (t_r_a == t_r_b) begin
t_e_address = reg_pc + t_d_imm;
t_e_branch = 1;
end
end
{ `INS_BRANCH, `INS_BRANCH_NE, `F7_DONT_CARE }: begin
if (t_r_a != t_r_b) begin
t_e_address = reg_pc + t_d_imm;
t_e_branch = 1;
end
end
{ `INS_BRANCH, `INS_BRANCH_LT, `F7_DONT_CARE }: begin
if ($signed(t_r_a) < $signed(t_r_b)) begin
t_e_address = reg_pc + t_d_imm;
t_e_branch = 1;
end
end
{ `INS_BRANCH, `INS_BRANCH_GE, `F7_DONT_CARE }: begin
if ($signed(t_r_a) >= $signed(t_r_b)) begin
t_e_address = reg_pc + t_d_imm;
t_e_branch = 1;
end
end
{ `INS_BRANCH, `INS_BRANCH_LTU, `F7_DONT_CARE }: begin
if (t_r_a < t_r_b) begin
t_e_address = reg_pc + t_d_imm;
t_e_branch = 1;
end
end
{ `INS_BRANCH, `INS_BRANCH_GEU, `F7_DONT_CARE }: begin
if (t_r_a >= t_r_b) begin
t_e_address = reg_pc + t_d_imm;
t_e_branch = 1;
end
end
2023-12-29 21:59:32 +00:00
{ `INS_LOAD, `F3_DONT_CARE, `F7_DONT_CARE }: begin
t_e_address = t_d_imm + t_r_a;
pmmu_addr = t_e_address;
pmmu_rd = 1;
#1;
pmmu_rd = 0;
case (t_d_f3[1:0])
0: t_e_result = (pmmu_datar >> (8 * (t_e_address & 3))) & 255;
1: begin
if (t_e_address[0] != 0) $error("UNALIGNED READ %08x", t_e_address);
t_e_result = (pmmu_datar >> (8 * (t_e_address & 2))) & 'hffff;
end
2: begin
if (t_e_address[1:0] != 0) $error("UNALIGNED READ %08x", t_e_address);
t_e_result = pmmu_datar;
end
3: $error("INVALID INSTRUCTION");
endcase
case (t_d_f3)
1,2,3,4:;
4: t_e_result = { {24{t_e_result[7]}}, t_e_result[7:0] };
5: t_e_result = { {16{t_e_result[16]}}, t_e_result[15:0] };
6, 7: $error("INVALID INSTRUCTION");
endcase
end
{ `INS_STORE, `F3_DONT_CARE, `F7_DONT_CARE }: begin
t_e_address = t_d_imm + t_r_a;
pmmu_addr = t_e_address;
pmmu_rd = 1;
#1;
case (t_d_f3)
0: begin
pmmu_dataw = pmmu_datar & ~('hff << (8 * t_e_address[1:0])) | (t_r_b & 'hff) << (8 * t_e_address[1:0]);
end
1: begin
if (t_e_address[0] != 0) $error("UNALIGNED STORE %08x", t_e_address);
pmmu_dataw = pmmu_datar & ~('hffff << (8 * t_e_address[1:0])) | (t_r_b & 'hffff) << (8 * t_e_address[1:0]);
end
2: begin
if (t_e_address[1:0] != 0) $error("UNALIGNED STORE %08x", t_e_address);
pmmu_dataw = t_r_b;
end
3, 4, 5, 6, 7: $error("INVALID INSTRUCTION");
endcase
pmmu_wr = 1;
pmmu_rd = 0;
end
2023-12-29 20:00:02 +00:00
{ `INS_IMMOP, `INS_ALUOP_ADD, `F7_DONT_CARE }: begin
t_e_result = $signed(t_r_a) + $signed(t_d_imm);
end
{ `INS_IMMOP, `INS_ALUOP_SLT, `F7_DONT_CARE }: begin
t_e_result = $signed(t_r_a) < $signed(t_d_imm) ? 1 : 0;
end
{ `INS_IMMOP, `INS_ALUOP_SLTU, `F7_DONT_CARE }: begin
t_e_result = t_r_a < t_d_imm ? 1 : 0;
end
{ `INS_IMMOP, `INS_ALUOP_XOR, `F7_DONT_CARE }: begin
t_e_result = t_r_a ^ t_d_imm;
end
{ `INS_IMMOP, `INS_ALUOP_OR, `F7_DONT_CARE }: begin
t_e_result = t_r_a | t_d_imm;
end
{ `INS_IMMOP, `INS_ALUOP_AND, `F7_DONT_CARE }: begin
t_e_result = t_r_a & t_d_imm;
end
{ `INS_IMMOP, `INS_ALUOP_SLL, 7'b0000000 }: begin
t_e_result = t_r_a << (t_d_imm & 31);
end
{ `INS_IMMOP, `INS_ALUOP_SRL, 7'b0000000 }: begin
t_e_result = t_r_a >> (t_d_imm & 31);
end
{ `INS_IMMOP, `INS_ALUOP_SRL, 7'b0100000 }: begin
t_e_result = t_r_a >>> (t_d_imm & 31);
end
{ `INS_REGOP, `INS_ALUOP_ADD, 7'b0000000 }: begin
t_e_result = $signed(t_r_a) + $signed(t_r_b);
end
{ `INS_REGOP, `INS_ALUOP_ADD, 7'b0100000 }: begin
t_e_result = $signed(t_r_a) - $signed(t_r_b);
end
{ `INS_REGOP, `INS_ALUOP_SLT, 7'b0000000 }: begin
t_e_result = $signed(t_r_a) < $signed(t_r_b) ? 1 : 0;
end
{ `INS_REGOP, `INS_ALUOP_SLTU, 7'b0000000 }: begin
t_e_result = t_r_a < t_r_b ? 1 : 0;
end
{ `INS_REGOP, `INS_ALUOP_XOR, 7'b0000000 }: begin
t_e_result = t_r_a ^ t_r_b;
end
{ `INS_REGOP, `INS_ALUOP_OR, 7'b0000000 }: begin
t_e_result = t_r_a | t_r_b;
end
{ `INS_REGOP, `INS_ALUOP_AND, 7'b0000000 }: begin
t_e_result = t_r_a & t_r_b;
end
{ `INS_REGOP, `INS_ALUOP_SLL, 7'b0000000 }: begin
t_e_result = t_r_a << (t_r_b & 31);
end
{ `INS_REGOP, `INS_ALUOP_SRL, 7'b0000000 }: begin
t_e_result = t_r_a >> (t_r_b & 31);
end
{ `INS_REGOP, `INS_ALUOP_SRL, 7'b0100000 }: begin
t_e_result = t_r_a >>> (t_r_b & 31);
end
{ `INS_FENCE, `F3_DONT_CARE, `F7_DONT_CARE }:;
{ `INS_ENVOP, `F3_DONT_CARE, `F7_DONT_CARE }:;
default: $error("INVALID INSTRUCTION");
endcase
2023-12-29 14:10:10 +00:00
2023-12-29 21:59:32 +00:00
// write back (ignore the delay, its not important)
@(posedge clk); #1;
2023-12-29 14:10:10 +00:00
$display("== WRITE REGISTERS ==");
2023-12-29 18:45:02 +00:00
case (t_d_op)
2023-12-29 20:00:02 +00:00
`INS_BRANCH, `INS_STORE, `INS_FENCE, `INS_ENVOP:;
default: begin
2023-12-29 18:45:02 +00:00
if (t_d_rd != 0) reg_int[t_d_rd] = t_e_result;
end
endcase
2023-12-29 14:10:10 +00:00
// finish up
2023-12-29 20:00:02 +00:00
reg_pc = t_e_branch ? t_e_address : reg_pc + 4;
2023-12-29 21:59:32 +00:00
// shhhh... you dont see this...
pmmu_rd = 0;
pmmu_wr = 0;
pmmu_addr = 0;
pmmu_dataw = 0;
2023-12-29 14:10:10 +00:00
end
endmodule