Add and use a 'badbit' RAM for ICache tests

This does nothing by default, just wrapping up a prim_generic_ram_1p.
But we can bind an interface into it to inject bit errors by forcing
the bad_bit_mask signal.

Note that the icache uses ECC RAMs in a reasonably unusual way (ORing
together inputs and outputs from its data RAMs), so we have to do this
ourselves, rather than piggy-backing on the implementation or testing
done for e.g. OpenTitan's prim_ram_1p_adv.
This commit is contained in:
Rupert Swarbrick 2020-06-12 15:51:27 +01:00 committed by Rupert Swarbrick
parent 8a145a9330
commit 48fbea833f
4 changed files with 119 additions and 0 deletions

View file

@ -20,6 +20,7 @@ targets:
sim:
parameters:
- ICacheECC
- PRIM_DEFAULT_IMPL=prim_pkg::ImplBadbit
filesets:
- files_rtl
- files_dv
@ -32,3 +33,9 @@ parameters:
default: 0
paramtype: vlogparam
description: "Enable ECC protection in instruction cache"
PRIM_DEFAULT_IMPL:
datatype: str
paramtype: vlogdefine
description: Primitives implementation to use, e.g. "prim_pkg::ImplGeneric".
default: prim_pkg::ImplBadbit

View file

@ -0,0 +1,11 @@
Badbit RAM
==========
This is an SRAM wrapper that allows a testbench to force bit errors onthe read interface.
This works as a dummy technology library.
Instantiate it by adding setting `PRIM_DEFAULT_IMPL` to prim_pkg::ImplBadbit (see the README.md in the prim directory for details).
To use it, bind a module or interface into an instance of `prim_badbit_ram_1p` and force the value of `bad_bit_mask`, which is XOR'd with rdata.
To make this easier to use, we don't vary the width of `bad_bit_mask` with the `Width` parameter: it's a constant 128.
This means that the bound interface doesn't need to be parameterised.

View file

@ -0,0 +1,20 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:prim_badbit:ram_1p"
description: "Single-port RAM which allows a bound interface to inject errors"
filesets:
files_rtl:
depend:
- lowrisc:prim_generic:ram_1p
- lowrisc:prim:assert
files:
- prim_badbit_ram_1p.sv
file_type: systemVerilogSource
targets:
default:
filesets:
- files_rtl

View file

@ -0,0 +1,81 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Single-port SRAM model which allows a test to corrupt read responses from the underlying memory.
//
// To use this, instantiate it then bind in a module or interface that has bad_bit_mask as an
// output. A nonzero bit in bad_bit_mask will cause the corresponding bit to be flipped in the
// response.
`include "prim_assert.sv"
module prim_badbit_ram_1p #(
parameter int Width = 32, // bit
parameter int Depth = 128,
parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask
parameter MemInitFile = "", // VMEM file to initialize the memory with
localparam int Aw = $clog2(Depth) // derived parameter
) (
input logic clk_i,
input logic req_i,
input logic write_i,
input logic [Aw-1:0] addr_i,
input logic [Width-1:0] wdata_i,
input logic [Width-1:0] wmask_i,
output logic [Width-1:0] rdata_o // Read data. Data is returned one cycle after req_i is high.
);
logic [Width-1:0] sram_rdata;
prim_generic_ram_1p #(
.Width (Width),
.Depth (Depth),
.DataBitsPerMask (DataBitsPerMask),
.MemInitFile (MemInitFile)
) u_mem (
.clk_i (clk_i),
.req_i (req_i),
.write_i (write_i),
.addr_i (addr_i),
.wdata_i (wdata_i),
.wmask_i (wmask_i),
.rdata_o (sram_rdata)
);
// This module doesn't work with Verilator (because of the wired-or). Because we define the
// "badbit" ram as a technology library, it gets picked up and parsed by any tool using the Ibex
// repo. Rather than telling Verilator to ignore the whole lot (which causes NOTFOUNDMODULE
// warnings), we just hide the actual guts.
`ifdef VERILATOR
assign rdata_o = sram_rdata;
`else
// Making bad_bit_mask a constant size makes this easier to use (because you don't need to faff
// around with parameterised interfaces in your UVM database). Check that rdata_o is actually
// controllable. Similarly, we make the address a constant width: make sure that's large enough.
`ASSERT_INIT(WidthSmallEnough, Width <= 128)
`ASSERT_INIT(AddrSmallEnough, Aw <= 32)
// Make the Width parameter easily accessible to bound-in modules.
logic [31:0] width;
assign width = Width;
// Similarly, extend addr and sram_rdata (the un-fiddled value)
logic [31:0] addr;
logic [127:0] rdata;
assign addr = {{32-Aw{1'b0}}, addr_i};
assign rdata = {{128-Width{1'b0}}, sram_rdata};
// To inject errors, bind in an interface with bad_bit_mask as an output and assign one of the
// bits in bad_bit_mask[Width-1:0] to one. The wired-OR together with an assignment to zero means
// this acts like a weak pull-down.
wor [127:0] bad_bit_mask;
assign bad_bit_mask = 128'b0;
assign rdata_o = sram_rdata ^ bad_bit_mask;
`endif // VERILATOR
endmodule