Refine memory interface.

This commit is contained in:
colin.liang 2023-01-17 16:09:28 +08:00
parent 7f42e7f7a4
commit dfc2c47327
3 changed files with 242 additions and 199 deletions

View File

@ -33,7 +33,6 @@
`endif `endif
`define assert(assert_expr) empty_statement `define assert(assert_expr) empty_statement
`define FORMAL_KEEP
/*************************************************************** /***************************************************************
* picorv32 * picorv32
@ -44,22 +43,21 @@ module picorv32 #(
parameter [31:0] STACKADDR = 32'hffff_ffff parameter [31:0] STACKADDR = 32'hffff_ffff
) ( ) (
input clk, input clk,
resetn, input resetn,
output reg trap, output reg trap,
output reg mem_valid, output reg mem_valid,
output reg mem_instr,
input mem_ready, input mem_ready,
output reg [31:0] mem_addr, output [31:0] mem_addr,
output reg [31:0] mem_wdata, output [31:0] mem_wdata,
output reg [ 3:0] mem_wstrb, output [ 3:0] mem_wstrb,
input [31:0] mem_rdata, input [31:0] mem_rdata,
// Look-Ahead Interface // Look-Ahead Interface
output mem_la_read, output mem_la_read,
output mem_la_write, output mem_la_write,
output [31:0] mem_la_addr, output reg [31:0] mem_la_addr,
output reg [31:0] mem_la_wdata, output reg [31:0] mem_la_wdata,
output reg [ 3:0] mem_la_wstrb, output reg [ 3:0] mem_la_wstrb,
@ -85,21 +83,90 @@ module picorv32 #(
reg [63:0] count_cycle, count_instr; reg [63:0] count_cycle, count_instr;
reg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out; reg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out;
reg [4:0] reg_sh; wire [31:0] next_pc;
reg [ 4:0] reg_sh;
reg [31:0] next_insn_opcode;
assign pcpi_rs1 = reg_op1; assign pcpi_rs1 = reg_op1;
assign pcpi_rs2 = reg_op2; assign pcpi_rs2 = reg_op2;
wire [31:0] next_pc; wire mem_xfer;
reg [31:0] mem_rdata_q;
task empty_statement; reg [1:0] mem_wordsize;
// This task is used by the `assert directive in non-formal mode to reg [31:0] mem_rdata_word;
// avoid empty statement (which are unsupported by plain Verilog syntax).
begin assign mem_la_addr = (mem_do_prefetch || mem_do_r_inst) ? {next_pc[31:2], 2'b00} : {reg_op1[31:2], 2'b00};
wire [31:0] mem_rdata_latched;
assign mem_rdata_latched = mem_xfer ? mem_rdata : mem_rdata_q;
always @(posedge clk) begin
if (mem_xfer) begin
mem_rdata_q <= mem_rdata;
end end
endtask end
always @* begin
(* full_case *)
case (mem_wordsize)
0: begin
mem_la_wdata = reg_op2;
mem_la_wstrb = 4'b1111;
mem_rdata_word = mem_rdata;
end
1: begin
mem_la_wdata = {2{reg_op2[15:0]}};
mem_la_wstrb = reg_op1[1] ? 4'b1100 : 4'b0011;
case (reg_op1[1])
1'b0: mem_rdata_word = {16'b0, mem_rdata[15:0]};
1'b1: mem_rdata_word = {16'b0, mem_rdata[31:16]};
endcase
end
2: begin
mem_la_wdata = {4{reg_op2[7:0]}};
mem_la_wstrb = 4'b0001 << reg_op1[1:0];
case (reg_op1[1:0])
2'b00: mem_rdata_word = {24'b0, mem_rdata[7:0]};
2'b01: mem_rdata_word = {24'b0, mem_rdata[15:8]};
2'b10: mem_rdata_word = {24'b0, mem_rdata[23:16]};
2'b11: mem_rdata_word = {24'b0, mem_rdata[31:24]};
endcase
end
endcase
end
reg mem_do_prefetch;
reg mem_do_r_inst;
reg mem_do_rdata;
reg mem_do_wdata;
wire mem_done;
picorv32_memory memory (
.clk(clk),
.resetn(resetn),
.trap(trap),
.mem_valid(mem_valid),
.mem_ready(mem_ready),
.mem_xfer (mem_xfer),
.mem_addr (mem_addr),
.mem_wdata(mem_wdata),
.mem_wstrb(mem_wstrb),
.mem_rdata(mem_rdata),
.mem_la_read (mem_la_read),
.mem_la_write(mem_la_write),
.mem_la_addr (mem_la_addr),
.mem_la_wdata(mem_la_wdata),
.mem_la_wstrb(mem_la_wstrb),
.mem_do_prefetch(mem_do_prefetch),
.mem_do_r_inst(mem_do_r_inst),
.mem_do_rdata(mem_do_rdata),
.mem_do_wdata(mem_do_wdata),
.mem_done(mem_done)
);
// Internal PCPI Cores // Internal PCPI Cores
@ -164,160 +231,6 @@ module picorv32 #(
endcase endcase
end end
// Memory Interface
reg [1:0] mem_state;
reg [1:0] mem_wordsize;
reg [31:0] mem_rdata_word;
reg [31:0] mem_rdata_q;
reg mem_do_prefetch;
reg mem_do_r_inst;
reg mem_do_rdata;
reg mem_do_wdata;
wire mem_xfer;
reg last_mem_valid;
reg [15:0] mem_16bit_buffer;
wire [31:0] mem_rdata_latched_noshuffle;
wire [31:0] mem_rdata_latched;
assign mem_xfer = mem_valid && mem_ready;
wire mem_busy = |{mem_do_prefetch, mem_do_r_inst, mem_do_rdata, mem_do_wdata};
wire mem_done = resetn && ((mem_xfer && |mem_state && (mem_do_r_inst || mem_do_rdata || mem_do_wdata)) || (&mem_state && mem_do_r_inst));
assign mem_la_write = resetn && !mem_state && mem_do_wdata;
assign mem_la_read = resetn && ((!mem_state && (mem_do_r_inst || mem_do_prefetch || mem_do_rdata)));
assign mem_la_addr = (mem_do_prefetch || mem_do_r_inst) ? {next_pc[31:2], 2'b00} : {reg_op1[31:2], 2'b00};
assign mem_rdata_latched_noshuffle = mem_xfer ? mem_rdata : mem_rdata_q;
assign mem_rdata_latched = mem_rdata_latched_noshuffle;
always @(posedge clk) begin
if (!resetn) begin
last_mem_valid <= 0;
end else begin
if (!last_mem_valid) last_mem_valid <= mem_valid && !mem_ready;
end
end
always @* begin
(* full_case *)
case (mem_wordsize)
0: begin
mem_la_wdata = reg_op2;
mem_la_wstrb = 4'b1111;
mem_rdata_word = mem_rdata;
end
1: begin
mem_la_wdata = {2{reg_op2[15:0]}};
mem_la_wstrb = reg_op1[1] ? 4'b1100 : 4'b0011;
case (reg_op1[1])
1'b0: mem_rdata_word = {16'b0, mem_rdata[15:0]};
1'b1: mem_rdata_word = {16'b0, mem_rdata[31:16]};
endcase
end
2: begin
mem_la_wdata = {4{reg_op2[7:0]}};
mem_la_wstrb = 4'b0001 << reg_op1[1:0];
case (reg_op1[1:0])
2'b00: mem_rdata_word = {24'b0, mem_rdata[7:0]};
2'b01: mem_rdata_word = {24'b0, mem_rdata[15:8]};
2'b10: mem_rdata_word = {24'b0, mem_rdata[23:16]};
2'b11: mem_rdata_word = {24'b0, mem_rdata[31:24]};
endcase
end
endcase
end
always @(posedge clk) begin
if (mem_xfer) begin
mem_rdata_q <= mem_rdata;
next_insn_opcode <= mem_rdata;
end
end
always @(posedge clk) begin
if (resetn && !trap) begin
if (mem_do_prefetch || mem_do_r_inst || mem_do_rdata)
`assert(!mem_do_wdata);
if (mem_do_prefetch || mem_do_r_inst)
`assert(!mem_do_rdata);
if (mem_do_rdata)
`assert(!mem_do_prefetch && !mem_do_r_inst);
if (mem_do_wdata)
`assert(!(mem_do_prefetch || mem_do_r_inst || mem_do_rdata));
if (mem_state == 2 || mem_state == 3)
`assert(mem_valid || mem_do_prefetch);
end
end
always @(posedge clk) begin
if (!resetn || trap) begin
if (!resetn) mem_state <= 0;
if (!resetn || mem_ready) mem_valid <= 0;
end else begin
if (mem_la_read || mem_la_write) begin
mem_addr <= mem_la_addr;
mem_wstrb <= mem_la_wstrb & {4{mem_la_write}};
end
if (mem_la_write) begin
mem_wdata <= mem_la_wdata;
end
case (mem_state)
0: begin
if (mem_do_prefetch || mem_do_r_inst || mem_do_rdata) begin
mem_valid <= 1;
mem_instr <= mem_do_prefetch || mem_do_r_inst;
mem_wstrb <= 0;
mem_state <= 1;
end
if (mem_do_wdata) begin
mem_valid <= 1;
mem_instr <= 0;
mem_state <= 2;
end
end
1: begin
`assert(mem_wstrb == 0);
`assert(mem_do_prefetch || mem_do_r_inst || mem_do_rdata);
`assert(mem_valid == 1);
`assert(mem_instr == (mem_do_prefetch || mem_do_r_inst));
if (mem_xfer) begin
mem_valid <= 0;
mem_state <= mem_do_r_inst || mem_do_rdata ? 0 : 3;
end
end
2: begin
`assert(mem_wstrb != 0);
`assert(mem_do_wdata);
if (mem_xfer) begin
mem_valid <= 0;
mem_state <= 0;
end
end
3: begin
`assert(mem_wstrb == 0);
`assert(mem_do_prefetch);
if (mem_do_r_inst) begin
mem_state <= 0;
end
end
endcase
end
end
// Memory Interface End
// Instruction Decoder // Instruction Decoder
reg instr_lui, instr_auipc, instr_jal, instr_jalr; reg instr_lui, instr_auipc, instr_jal, instr_jalr;
@ -459,8 +372,8 @@ module picorv32 #(
if (decoder_trigger_q) begin if (decoder_trigger_q) begin
cached_ascii_instr <= new_ascii_instr; cached_ascii_instr <= new_ascii_instr;
cached_insn_imm <= decoded_imm; cached_insn_imm <= decoded_imm;
if (&next_insn_opcode[1:0]) cached_insn_opcode <= next_insn_opcode; if (&mem_rdata_q[1:0]) cached_insn_opcode <= mem_rdata_q;
else cached_insn_opcode <= {16'b0, next_insn_opcode[15:0]}; else cached_insn_opcode <= {16'b0, mem_rdata_q[15:0]};
cached_insn_rs1 <= decoded_rs1; cached_insn_rs1 <= decoded_rs1;
cached_insn_rs2 <= decoded_rs2; cached_insn_rs2 <= decoded_rs2;
cached_insn_rd <= decoded_rd; cached_insn_rd <= decoded_rd;
@ -489,8 +402,8 @@ module picorv32 #(
dbg_insn_rd = cached_insn_rd; dbg_insn_rd = cached_insn_rd;
end else begin end else begin
dbg_ascii_instr = new_ascii_instr; dbg_ascii_instr = new_ascii_instr;
if (&next_insn_opcode[1:0]) dbg_insn_opcode = next_insn_opcode; if (&mem_rdata_q[1:0]) dbg_insn_opcode = mem_rdata_q;
else dbg_insn_opcode = {16'b0, next_insn_opcode[15:0]}; else dbg_insn_opcode = {16'b0, mem_rdata_q[15:0]};
dbg_insn_imm = decoded_imm; dbg_insn_imm = decoded_imm;
dbg_insn_rs1 = decoded_rs1; dbg_insn_rs1 = decoded_rs1;
dbg_insn_rs2 = decoded_rs2; dbg_insn_rs2 = decoded_rs2;
@ -789,7 +702,7 @@ module picorv32 #(
set_mem_do_rdata = 0; set_mem_do_rdata = 0;
set_mem_do_wdata = 0; set_mem_do_wdata = 0;
alu_out_q <= alu_out; alu_out_q <= alu_out;
if (launch_next_insn) begin if (launch_next_insn) begin
dbg_rs1val <= 'bx; dbg_rs1val <= 'bx;
@ -1359,3 +1272,141 @@ module picorv32_pcpi_div (
end end
end end
endmodule endmodule
/***************************************************************
* picorv32_memory
***************************************************************/
module picorv32_memory (
input clk,
input resetn,
input trap,
output reg mem_valid,
input mem_ready,
output reg mem_xfer,
output reg [31:0] mem_addr,
output reg [31:0] mem_wdata,
output reg [ 3:0] mem_wstrb,
input [31:0] mem_rdata,
// Look-Ahead Interface
output mem_la_read,
output mem_la_write,
input [31:0] mem_la_addr,
input [31:0] mem_la_wdata,
input [ 3:0] mem_la_wstrb,
input mem_do_prefetch,
input mem_do_r_inst,
input mem_do_rdata,
input mem_do_wdata,
output mem_done
);
task empty_statement;
// This task is used by the `assert directive in non-formal mode to
// avoid empty statement (which are unsupported by plain Verilog syntax).
begin
end
endtask
reg [1:0] mem_state;
reg last_mem_valid;
reg [15:0] mem_16bit_buffer;
reg mem_instr;
assign mem_xfer = mem_valid && mem_ready;
wire mem_busy = |{mem_do_prefetch, mem_do_r_inst, mem_do_rdata, mem_do_wdata};
assign mem_done = resetn && ((mem_xfer && |mem_state && (mem_do_r_inst || mem_do_rdata || mem_do_wdata)) || (&mem_state && mem_do_r_inst));
assign mem_la_write = resetn && !mem_state && mem_do_wdata;
assign mem_la_read = resetn && ((!mem_state && (mem_do_r_inst || mem_do_prefetch || mem_do_rdata)));
always @(posedge clk) begin
if (!resetn) begin
last_mem_valid <= 0;
end else begin
if (!last_mem_valid) last_mem_valid <= mem_valid && !mem_ready;
end
end
always @(posedge clk) begin
if (resetn && !trap) begin
if (mem_do_prefetch || mem_do_r_inst || mem_do_rdata)
`assert(!mem_do_wdata);
if (mem_do_prefetch || mem_do_r_inst)
`assert(!mem_do_rdata);
if (mem_do_rdata)
`assert(!mem_do_prefetch && !mem_do_r_inst);
if (mem_do_wdata)
`assert(!(mem_do_prefetch || mem_do_r_inst || mem_do_rdata));
if (mem_state == 2 || mem_state == 3)
`assert(mem_valid || mem_do_prefetch);
end
end
always @(posedge clk) begin
if (!resetn || trap) begin
if (!resetn) mem_state <= 0;
if (!resetn || mem_ready) mem_valid <= 0;
end else begin
if (mem_la_read || mem_la_write) begin
mem_addr <= mem_la_addr;
mem_wstrb <= mem_la_wstrb & {4{mem_la_write}};
end
if (mem_la_write) begin
mem_wdata <= mem_la_wdata;
end
case (mem_state)
0: begin
if (mem_do_prefetch || mem_do_r_inst || mem_do_rdata) begin
mem_valid <= 1;
mem_instr <= mem_do_prefetch || mem_do_r_inst;
mem_wstrb <= 0;
mem_state <= 1;
end
if (mem_do_wdata) begin
mem_valid <= 1;
mem_instr <= 0;
mem_state <= 2;
end
end
1: begin
`assert(mem_wstrb == 0);
`assert(mem_do_prefetch || mem_do_r_inst || mem_do_rdata);
`assert(mem_valid == 1);
`assert(mem_instr == (mem_do_prefetch || mem_do_r_inst));
if (mem_xfer) begin
mem_valid <= 0;
mem_state <= mem_do_r_inst || mem_do_rdata ? 0 : 3;
end
end
2: begin
`assert(mem_wstrb != 0);
`assert(mem_do_wdata);
if (mem_xfer) begin
mem_valid <= 0;
mem_state <= 0;
end
end
3: begin
`assert(mem_wstrb == 0);
`assert(mem_do_prefetch);
if (mem_do_r_inst) begin
mem_state <= 0;
end
end
endcase
end
end
endmodule

View File

@ -40,13 +40,13 @@ int main(int argc, char** argv, char** env) {
trace_fd = fopen("testbench.trace", "w"); trace_fd = fopen("testbench.trace", "w");
} }
top->wb_clk = 0; top->clk = 0;
top->wb_rst = 1; top->rst = 1;
int t = 0; int t = 0;
while (!Verilated::gotFinish()) { while (!Verilated::gotFinish()) {
if (t > 200) top->wb_rst = 0; if (t > 200) top->rst = 0;
top->wb_clk = !top->wb_clk; top->clk = !top->clk;
top->eval(); top->eval();
if (tfp) tfp->dump(t); if (tfp) tfp->dump(t);
t += 5; t += 5;

View File

@ -3,16 +3,15 @@
module picorv32_wrapper #( module picorv32_wrapper #(
parameter VERBOSE = 0 parameter VERBOSE = 0
) ( ) (
input wb_clk, input clk,
input wb_rst, input rst,
output trap, output trap,
input [1024:0] hex_file input [1024:0] hex_file
); );
wire exit; wire exit;
wire mem_instr;
reg [15:0] count_cycle = 0; reg [15:0] count_cycle = 0;
always @(posedge wb_clk) count_cycle <= !wb_rst ? count_cycle + 1 : 0; always @(posedge clk) count_cycle <= !rst ? count_cycle + 1 : 0;
wire [31:0] wb_m2s_adr; wire [31:0] wb_m2s_adr;
wire [31:0] wb_m2s_dat; wire [31:0] wb_m2s_dat;
@ -26,10 +25,9 @@ module picorv32_wrapper #(
picorv32_wb #() uut ( picorv32_wb #() uut (
.trap(trap), .trap(trap),
.exit(exit), .exit(exit),
.mem_instr(mem_instr),
.wb_clk_i(wb_clk), .clk(clk),
.wb_rst_i(wb_rst) .rst(rst)
); );
initial begin initial begin
@ -38,9 +36,9 @@ module picorv32_wrapper #(
end end
integer cycle_counter; integer cycle_counter;
always @(posedge wb_clk) begin always @(posedge clk) begin
cycle_counter <= !wb_rst ? cycle_counter + 1 : 0; cycle_counter <= !rst ? cycle_counter + 1 : 0;
if (!wb_rst && trap) begin if (!rst && trap) begin
$display("TRAP after %1d clock cycles", cycle_counter); $display("TRAP after %1d clock cycles", cycle_counter);
if (exit) begin if (exit) begin
$display("ALL TESTS PASSED."); $display("ALL TESTS PASSED.");
@ -59,11 +57,8 @@ module picorv32_wb #(
output trap, output trap,
output reg exit, output reg exit,
// Wishbone interfaces input rst,
input wb_rst_i, input clk
input wb_clk_i,
output mem_instr
); );
wire mem_valid; wire mem_valid;
wire [31:0] mem_addr; wire [31:0] mem_addr;
@ -84,12 +79,10 @@ module picorv32_wb #(
wire [31:0] dbg_insn_addr; wire [31:0] dbg_insn_addr;
wire [63:0] dbg_ascii_instr; wire [63:0] dbg_ascii_instr;
wire clk;
wire resetn; wire resetn;
initial exit = 0; initial exit = 0;
assign clk = wb_clk_i; assign resetn = ~rst;
assign resetn = ~wb_rst_i;
picorv32 #( picorv32 #(
.PROGADDR_RESET(32'h0001_0000), .PROGADDR_RESET(32'h0001_0000),
@ -100,7 +93,6 @@ module picorv32_wb #(
.trap (trap), .trap (trap),
.mem_valid (mem_valid), .mem_valid (mem_valid),
.mem_instr (mem_instr),
.mem_ready (mem_ready), .mem_ready (mem_ready),
.mem_addr (mem_addr), .mem_addr (mem_addr),
.mem_wdata (mem_wdata), .mem_wdata (mem_wdata),