Files
simbench/source/picorv32_tcm.sv

180 lines
5.7 KiB
Systemverilog
Raw Normal View History

2023-06-11 16:15:40 +03:00
`timescale 1ps/1ps
module picorv32_tcm #(parameter ADDR_WIDTH = 8,
parameter USE_LOOK_AHEAD = 0,
parameter USE_ADDR_MUX = 1,
parameter MEM_INIT_FILE = "")
(input wire clock,
/* verilator lint_off UNUSED */
// Not used in look-ahead mode
input wire reset,
/* verilator lint_on UNUSED */
/* PicoRV32 bus interface */
input wire mem_valid,
output wire mem_ready,
input wire [ADDR_WIDTH-1:0] mem_addr,
input wire [31:0] mem_wdata,
input wire [3:0] mem_wstrb,
output reg [31:0] mem_rdata,
// PicoRV32 look-ahead address.
// Not used in non-look-ahead mode.
/* verilator lint_off UNUSED */
input wire [ADDR_WIDTH-1:0] mem_la_addr
/* verilator lint_off UNUSED */
);
logic [31:0] ram[0:(2 ** (ADDR_WIDTH-2))-1];
if (MEM_INIT_FILE != "")
initial $readmemh(MEM_INIT_FILE, ram);
/* verilator lint_off UNUSED */
// Bits [1:0] are not used
logic [ADDR_WIDTH-1:0] byte_addr;
/* verilator lint_on UNUSED */
logic [ADDR_WIDTH-3:0] word_addr;
logic write;
assign word_addr = byte_addr[ADDR_WIDTH-1:2];
always_ff @(posedge clock) begin
for (int n = 0; n < 4; n += 1)
if (write && mem_wstrb[n])
ram[word_addr][n*8 +: 8] <= mem_wdata[n*8 +: 8];
mem_rdata <= ram[word_addr];
end
if (USE_LOOK_AHEAD == 0) begin : no_look_ahead
logic data_ready;
always_ff @(posedge clock)
if (reset) data_ready <= 1'b0;
else
// Don't use ternary operator to prevent
// X-propagation from PicoRV32 core
// data_ready <= mem_valid & ~(|mem_wstrb);
if (mem_valid && mem_wstrb == '0)
data_ready <= 1'b1;
else
data_ready <= 1'b0;
assign byte_addr = mem_addr;
assign write = mem_valid & (|mem_wstrb);
assign mem_ready = data_ready | write;
end
else begin : look_ahead
logic data_ready;
always_ff @(posedge clock)
if (reset) data_ready <= 1'b0;
else
// Don't use ternary operator to prevent
// X-propagation from PicoRV32 core
// data_ready <= ~(mem_valid && (|mem_wstrb));
if (mem_valid && mem_wstrb != '0)
data_ready <= 1'b0;
else
data_ready <= 1'b1;
/* mem_la_addr валиден как минимум один такт после поднятия mem_valid.
* Т.е. в принципе можно обойтись без мультиплескора. В формальной части
* добавлено соответствуюшее утверждение. */
if (USE_ADDR_MUX == 0)
assign byte_addr = mem_la_addr[ADDR_WIDTH-1:0];
else
assign byte_addr = mem_valid ?
mem_addr[ADDR_WIDTH-1:0] :
mem_la_addr[ADDR_WIDTH-1:0];
assign write = mem_valid & (|mem_wstrb);
assign mem_ready = data_ready;
end
`ifdef FORMAL
// Past valid flag
logic have_past = 1'b0;
always_ff @(posedge clock) have_past <= 1'b1;
// Assumptions
always @(*) assume(reset == 1'b0);
always @(posedge clock)
if (have_past) begin
// mem_addr <= mem_la_addr
if ($rose(mem_valid))
assume(mem_addr == $past(mem_la_addr));
// Stable mem_addr and mem_data when mem_valid is active
if (mem_valid) begin
assume($stable(mem_addr));
assume($stable(mem_wdata));
assume($stable(mem_wstrb));
end
// Assume mem_valid will not cleared while mem_ready is not active
if ($past(mem_valid) && !$past(mem_ready))
assume(mem_valid == 1'b1);
// Assume mem_valid will cleared after memory transaction complete
if ($past(mem_valid) && $past(mem_ready))
assume(mem_valid == 1'b0);
// WARN: May be wrong assumption
// Assume mem_add == mem_la_addr on first clock cycle of mem_valid activity
if ($rose(mem_valid))
assume(mem_addr == mem_la_addr);
end
else begin
// Initial mem_valid = 1'b0
assume(mem_valid == 1'b0);
end
// Data read
always_ff @(posedge clock)
if (have_past)
if (mem_valid && mem_ready && mem_wstrb == '0)
assert(mem_rdata == ram[mem_addr[ADDR_WIDTH-1:2]]);
// Data write
always_ff @(posedge clock) begin
logic [3:0] mem_wstrb_past;
if (have_past) begin
mem_wstrb_past = $past(mem_wstrb);
if ($past(mem_valid) && $past(mem_ready) && mem_wstrb_past != '0)
for (int n = 0; n < 4; n += 1)
if (mem_wstrb_past[n])
assert(ram[$past(mem_addr[ADDR_WIDTH-1:2])][n*8 +: 8] == $past(mem_wdata[n*8 +: 8]));
end
end
// Mem ready
always_ff @(posedge clock)
if (have_past)
if (USE_LOOK_AHEAD == 0) begin
// Write transaction
if (mem_wstrb != '0 && mem_valid)
assert(mem_ready);
// First cycle of read transaction
if (mem_wstrb == '0 && !$past(mem_valid)&& mem_valid)
assert(!mem_ready);
// Second cycle of read transaction
if ($past(mem_wstrb) == '0 && $past(mem_valid) && mem_valid)
assert(mem_ready);
end
else begin
// In look-ahead mode mem_ready always active
if (mem_valid)
assert(mem_ready);
end
`endif
endmodule // picorv32_tcm