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
`define assert(assert_expr) empty_statement
`define FORMAL_KEEP
/***************************************************************
* picorv32
@ -44,22 +43,21 @@ module picorv32 #(
parameter [31:0] STACKADDR = 32'hffff_ffff
) (
input clk,
resetn,
input resetn,
output reg trap,
output reg mem_valid,
output reg mem_instr,
input mem_ready,
output reg [31:0] mem_addr,
output reg [31:0] mem_wdata,
output reg [ 3:0] mem_wstrb,
output [31:0] mem_addr,
output [31:0] mem_wdata,
output [ 3:0] mem_wstrb,
input [31:0] mem_rdata,
// Look-Ahead Interface
output mem_la_read,
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 [ 3:0] mem_la_wstrb,
@ -85,21 +83,90 @@ module picorv32 #(
reg [63:0] count_cycle, count_instr;
reg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out;
wire [31:0] next_pc;
reg [ 4:0] reg_sh;
reg [31:0] next_insn_opcode;
assign pcpi_rs1 = reg_op1;
assign pcpi_rs2 = reg_op2;
wire [31:0] next_pc;
wire mem_xfer;
reg [31:0] mem_rdata_q;
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
reg [1:0] mem_wordsize;
reg [31:0] mem_rdata_word;
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
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
@ -164,160 +231,6 @@ module picorv32 #(
endcase
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
reg instr_lui, instr_auipc, instr_jal, instr_jalr;
@ -459,8 +372,8 @@ module picorv32 #(
if (decoder_trigger_q) begin
cached_ascii_instr <= new_ascii_instr;
cached_insn_imm <= decoded_imm;
if (&next_insn_opcode[1:0]) cached_insn_opcode <= next_insn_opcode;
else cached_insn_opcode <= {16'b0, next_insn_opcode[15:0]};
if (&mem_rdata_q[1:0]) cached_insn_opcode <= mem_rdata_q;
else cached_insn_opcode <= {16'b0, mem_rdata_q[15:0]};
cached_insn_rs1 <= decoded_rs1;
cached_insn_rs2 <= decoded_rs2;
cached_insn_rd <= decoded_rd;
@ -489,8 +402,8 @@ module picorv32 #(
dbg_insn_rd = cached_insn_rd;
end else begin
dbg_ascii_instr = new_ascii_instr;
if (&next_insn_opcode[1:0]) dbg_insn_opcode = next_insn_opcode;
else dbg_insn_opcode = {16'b0, next_insn_opcode[15:0]};
if (&mem_rdata_q[1:0]) dbg_insn_opcode = mem_rdata_q;
else dbg_insn_opcode = {16'b0, mem_rdata_q[15:0]};
dbg_insn_imm = decoded_imm;
dbg_insn_rs1 = decoded_rs1;
dbg_insn_rs2 = decoded_rs2;
@ -1359,3 +1272,141 @@ module picorv32_pcpi_div (
end
end
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");
}
top->wb_clk = 0;
top->wb_rst = 1;
top->clk = 0;
top->rst = 1;
int t = 0;
while (!Verilated::gotFinish()) {
if (t > 200) top->wb_rst = 0;
top->wb_clk = !top->wb_clk;
if (t > 200) top->rst = 0;
top->clk = !top->clk;
top->eval();
if (tfp) tfp->dump(t);
t += 5;

View File

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