Use vendored-in primitives from OpenTitan

Instead of using copies of primitives from OpenTitan, vendor the files
in directly from OpenTitan, and use them.

Benefits:

- Less potential for diverging code between OpenTitan and Ibex, causing
  problems when importing Ibex into OT.

- Use of the abstract primitives instead of the generic ones. The
  abstract primitives are replaced during synthesis time with
  target-dependent implementations. For simulation, nothing changes. For
  synthesis for a given target technology (e.g. a specific ASIC or FPGA
  technology), the primitives system can be instructed to choose
  optimized versions (if available).

  This is most relevant for the icache, which hard-coded the generic
  SRAM primitive before. This primitive is always implemented as
  registers. By using the abstract primitive (prim_ram_1p) instead, the
  RAMs can be replaced with memory-compiler-generated ones if necessary.

There are no real draw-backs, but a couple points to be aware of:

- Our ram_1p and ram_2p implementations are kept as wrapper around the
  primitives, since their interface deviates slightly from the one in
  prim_ram*. This also includes a rather unfortunate naming confusion
  around rvalid, which means "read data valid" in the OpenTitan advanced
  RAM primitives (prim_ram_1p_adv for example), but means "ack" in
  PULP-derived IP and in our bus implementation.

- The core_ibex UVM DV doesn't use FuseSoC to generate its file list,
  but uses a hard-coded list in `ibex_files.f` instead. Since the
  dynamic primitives system requires the use of FuseSoC we need to
  provide a stop-gap until this file is removed. Issue #893 tracks
  progress on that.

- Dynamic primitives depend no a not-yet-merged feature of FuseSoC
  (https://github.com/olofk/fusesoc/pull/391). We depend on the same
  functionality in OpenTitan and have instructed users to use a patched
  branch of FuseSoC for a long time through `python-requirements.txt`,
  so no action is needed for users which are either successfully
  interacting with the OpenTitan source code, or have followed our
  instructions. All other users will see a reasonably descriptive error
  message during a FuseSoC run.

- This commit is massive, but there are no good ways to split it into
  bisectable, yet small, chunks. I'm sorry. Reviewers can safely ignore
  all code in `vendor/lowrisc_ip`, it's an import from OpenTitan.

- The check_tool_requirements tooling isn't easily vendor-able from
  OpenTitan at the moment. I've filed
  https://github.com/lowRISC/opentitan/issues/2309 to get that sorted.

- The LFSR primitive doesn't have a own core file, forcing us to include
  the catch-all `lowrisc:prim:all` core. I've filed
  https://github.com/lowRISC/opentitan/issues/2310 to get that sorted.
This commit is contained in:
Philipp Wagner 2020-05-11 17:19:14 +01:00 committed by Philipp Wagner
parent 3f4e706062
commit 8b42024cd5
190 changed files with 11808 additions and 252 deletions

View file

@ -2,7 +2,7 @@ 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:ibex:check_tool_requirements:0.1"
name: "lowrisc:tool:check_tool_requirements:0.1"
description: "Check tool requirements"
filesets:

View file

@ -13,7 +13,9 @@ int main(int argc, char **argv) {
simctrl.SetTop(&top, &top.IO_CLK, &top.IO_RST_N,
VerilatorSimCtrlFlags::ResetPolarityNegative);
memutil.RegisterMemoryArea("ram", "TOP.ibex_riscv_compliance.u_ram.u_ram");
memutil.RegisterMemoryArea(
"ram",
"TOP.ibex_riscv_compliance.u_ram.u_ram.gen_generic.u_impl_generic");
simctrl.RegisterExtension(&memutil);
return simctrl.Exec(argc, argv);

View file

@ -0,0 +1,35 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Abstract primitives wrapper.
//
// This file is a stop-gap until the DV file list is generated by FuseSoC.
// Its contents are taken from the file which would be generated by FuseSoC.
// https://github.com/lowRISC/ibex/issues/893
`ifndef PRIM_DEFAULT_IMPL
`define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric
`endif
module prim_clock_gating (
input clk_i,
input en_i,
input test_en_i,
output logic clk_o
);
parameter prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL;
if (Impl == prim_pkg::ImplGeneric) begin : gen_generic
prim_generic_clock_gating u_impl_generic (
.*
);
end else if (Impl == prim_pkg::ImplXilinx) begin : gen_xilinx
prim_xilinx_clock_gating u_impl_xilinx (
.*
);
end else begin : gen_failure
// TODO: Find code that works across tools and causes a compile failure
end
endmodule

View file

@ -0,0 +1,18 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Constants for use in primitives
//
// This file is a stop-gap until the DV file list is generated by FuseSoC.
// Its contents are taken from the file which would be generated by FuseSoC.
// https://github.com/lowRISC/ibex/issues/893
package prim_pkg;
// Implementation target specialization
typedef enum integer {
ImplGeneric,
ImplXilinx
} impl_e;
endpackage : prim_pkg

View file

@ -0,0 +1,40 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Abstract primitives wrapper.
//
// This file is a stop-gap until the DV file list is generated by FuseSoC.
// Its contents are taken from the file which would be generated by FuseSoC.
// https://github.com/lowRISC/ibex/issues/893
`ifndef PRIM_DEFAULT_IMPL
`define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric
`endif
module prim_ram_1p #(
parameter int Width = 32, // bit
parameter int Depth = 128,
parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask
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.
);
parameter prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL;
if (Impl == prim_pkg::ImplGeneric) begin : gen_generic
prim_generic_ram_1p u_impl_generic (
.*
);
end else begin : gen_failure
// TODO: Find code that works across tools and causes a compile failure
end
endmodule

View file

@ -7,19 +7,27 @@
+define+BOOT_ADDR=2147483648 // 32'h8000_0000
+define+TRACE_EXECUTION
+define+RVFI
+incdir+${PRJ_DIR}/ibex/shared/rtl
${PRJ_DIR}/ibex/shared/rtl/prim_clock_gating.sv
// Shared lowRISC code
+incdir+${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl
${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_assert.sv
${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_lfsr.sv
${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_secded_28_22_enc.sv
${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_secded_28_22_dec.sv
${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_secded_72_64_enc.sv
${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_secded_72_64_dec.sv
// Until this list is generated by FuseSoC, we have to use manually generated
// wrappers around the prim_* modules to instantiate the prim_generic_* ones,
// see https://github.com/lowRISC/ibex/issues/893.
${PRJ_DIR}/ibex/dv/uvm/core_ibex/common/prim/prim_pkg.sv
${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_1p.sv
${PRJ_DIR}/ibex/dv/uvm/core_ibex/common/prim/prim_ram_1p.sv
${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_gating.sv
${PRJ_DIR}/ibex/dv/uvm/core_ibex/common/prim/prim_clock_gating.sv
// ibex CORE RTL files
+incdir+${PRJ_DIR}/ibex/rtl
${PRJ_DIR}/ibex/shared/rtl/prim_assert.sv
${PRJ_DIR}/ibex/shared/rtl/prim_generic_ram_1p.sv
${PRJ_DIR}/ibex/shared/rtl/prim_lfsr.sv
${PRJ_DIR}/ibex/shared/rtl/prim_secded_28_22_enc.sv
${PRJ_DIR}/ibex/shared/rtl/prim_secded_28_22_dec.sv
${PRJ_DIR}/ibex/shared/rtl/prim_secded_72_64_enc.sv
${PRJ_DIR}/ibex/shared/rtl/prim_secded_72_64_dec.sv
${PRJ_DIR}/ibex/rtl/ibex_pkg.sv
${PRJ_DIR}/ibex/rtl/ibex_tracer_pkg.sv
${PRJ_DIR}/ibex/rtl/ibex_tracer.sv

View file

@ -17,7 +17,8 @@ int main(int argc, char **argv) {
simctrl.SetTop(&top, &top.IO_CLK, &top.IO_RST_N,
VerilatorSimCtrlFlags::ResetPolarityNegative);
memutil.RegisterMemoryArea("ram", "TOP.ibex_simple_system.u_ram");
memutil.RegisterMemoryArea(
"ram", "TOP.ibex_simple_system.u_ram.u_ram.gen_generic.u_impl_generic");
simctrl.RegisterExtension(&memutil);
std::cout << "Simulation of Ibex" << std::endl

View file

@ -9,7 +9,9 @@ filesets:
files_rtl:
depend:
- lowrisc:prim:assert
- lowrisc:prim:lfsr
# TODO: Only lfsr is needed. Replace with a more specific dependency
# once available.
- lowrisc:prim:all
- lowrisc:ibex:ibex_pkg
- lowrisc:ibex:ibex_icache
files:
@ -48,7 +50,7 @@ filesets:
files_check_tool_requirements:
depend:
- lowrisc:ibex:check_tool_requirements
- lowrisc:tool:check_tool_requirements
parameters:
RVFI:

View file

@ -152,3 +152,7 @@ lint_off -rule UNUSED -file "*/rtl/ibex_decoder.sv" -match "*instr_alu*"
// ibex_core.cs_registers_i.mie_q
// Issue lowrisc/ibex#212
lint_off -rule UNOPTFLAT -file "*/rtl/ibex_cs_registers.sv" -match "*ibex_core.cs_registers_i.mie_q*"
// Temporary waivers until OpenTitan primitives are lint-clean
// https://github.com/lowRISC/opentitan/issues/2313
lint_off -file "*/lowrisc_prim_*/rtl/*.sv"

View file

@ -9,3 +9,4 @@ git+https://github.com/lowRISC/edalize.git@ot
git+https://github.com/lowRISC/fusesoc.git@ot
pyyaml
mako

View file

@ -300,33 +300,29 @@ module ibex_icache #(
for (genvar way = 0; way < NumWays; way++) begin : gen_rams
// Tag RAM instantiation
prim_generic_ram_1p #(
prim_ram_1p #(
.Width (TAG_SIZE_ECC),
.Depth (NUM_LINES)
) tag_bank (
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (tag_req_ic0 & tag_banks_ic0[way]),
.write_i (tag_write_ic0),
.wmask_i ({TAG_SIZE_ECC{1'b1}}),
.addr_i (tag_index_ic0),
.wdata_i (tag_wdata_ic0),
.rvalid_o (),
.rdata_o (tag_rdata_ic1[way])
);
// Data RAM instantiation
prim_generic_ram_1p #(
prim_ram_1p #(
.Width (LINE_SIZE_ECC),
.Depth (NUM_LINES)
) data_bank (
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (data_req_ic0 & data_banks_ic0[way]),
.write_i (data_write_ic0),
.wmask_i ({LINE_SIZE_ECC{1'b1}}),
.addr_i (data_index_ic0),
.wdata_i (data_wdata_ic0),
.rvalid_o (),
.rdata_o (data_rdata_ic1[way])
);
end

View file

@ -6,8 +6,9 @@ name: "lowrisc:ibex:fpga_xilinx_shared"
description: "Collection of useful RTL for Xilinx based examples"
filesets:
files_sv:
depend:
- lowrisc:prim:clock_gating
files:
- rtl/fpga/xilinx/prim_clock_gating.sv
- rtl/fpga/xilinx/clkgen_xil7series.sv
- rtl/ram_1p.sv
file_type: systemVerilogSource

View file

@ -1,24 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Dummy clock gating module compatible with latch-based register file
module prim_clock_gating (
input clk_i,
input en_i,
input test_en_i,
output logic clk_o
);
logic clk_en;
always_latch begin
if (clk_i == 1'b0) begin
clk_en <= en_i | test_en_i;
end
end
assign clk_o = clk_i & clk_en;
endmodule

View file

@ -1,109 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Synchronous single-port SRAM model
`include "prim_assert.sv"
module prim_generic_ram_1p #(
parameter int Width = 32, // bit
parameter int Depth = 128,
parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask
localparam int Aw = $clog2(Depth) // derived parameter
) (
input logic clk_i,
input logic rst_ni,
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 rvalid_o,
output logic [Width-1:0] rdata_o
);
// Width of internal write mask. Note wmask_i input into the module is always assumed
// to be the full bit mask
localparam int MaskWidth = Width / DataBitsPerMask;
logic [Width-1:0] mem [Depth];
logic [MaskWidth-1:0] wmask;
always_comb begin
for (int i=0; i < MaskWidth; i = i + 1) begin : create_wmask
wmask[i] = &wmask_i[i*DataBitsPerMask +: DataBitsPerMask];
end
end
// using always instead of always_ff to avoid 'ICPD - illegal combination of drivers' error
// thrown when using $readmemh system task to backdoor load an image
always @(posedge clk_i) begin
if (req_i) begin
if (write_i) begin
for (int i=0; i < MaskWidth; i = i + 1) begin
if (wmask[i]) begin
mem[addr_i][i*DataBitsPerMask +: DataBitsPerMask] <=
wdata_i[i*DataBitsPerMask +: DataBitsPerMask];
end
end
end else begin
rdata_o <= mem[addr_i];
end
end
end
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni) begin
rvalid_o <= '0;
end else begin
rvalid_o <= req_i & ~write_i;
end
end
`ifdef VERILATOR
// Task for loading 'mem' with SystemVerilog system task $readmemh()
export "DPI-C" task simutil_verilator_memload;
task simutil_verilator_memload;
input string file;
$readmemh(file, mem);
endtask
// Width must be a multiple of 32bit for this function to work
// Note that the DPI export and function definition must both be in the same generate
// context to get the correct name.
if ((Width % 32) == 0) begin : gen_set_mem
// Function for setting a specific element in |mem|
// Returns 1 (true) for success, 0 (false) for errors.
export "DPI-C" function simutil_verilator_set_mem;
function int simutil_verilator_set_mem(input int index,
input bit [Width-1:0] val);
if (index >= Depth) begin
return 0;
end
mem[index] = val;
return 1;
endfunction
end else begin : gen_other
// Function doesn't work unless Width % 32 so just return 0
export "DPI-C" function simutil_verilator_set_mem;
function int simutil_verilator_set_mem(input int index,
input bit [Width-1:0] val);
return 0;
endfunction
end
`endif
`ifdef SRAM_INIT_FILE
localparam MEM_FILE = `PRIM_STRINGIFY(`SRAM_INIT_FILE);
initial begin
$display("Initializing SRAM from %s", MEM_FILE);
$readmemh(MEM_FILE, mem);
end
`endif
endmodule

View file

@ -39,32 +39,28 @@ module ram_1p #(
end
end
// |rvalid| in the bus module is an "ack", while prim_ram_1p associates the
// meaning "returned read data is valid" with this signal.
// Convert the RAM meaning to the meaning assumed by the bus module.
logic read_valid, we_q;
// |rvalid| in the bus module is an "ack" (which is a different meaning than
// in the prim_ram_*_adv modules). prim_ram_1p is guaranteed to return data
// in the next cycle.
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni) begin
we_q <= 1'b0;
rvalid_o <= 1'b0;
end else begin
we_q <= we_i;
rvalid_o <= req_i;
end
end
assign rvalid_o = read_valid | we_q;
prim_generic_ram_1p #(
prim_ram_1p #(
.Width(32),
.DataBitsPerMask(8),
.Depth(Depth)
) u_ram (
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (req_i),
.write_i (we_i),
.wmask_i (wmask),
.addr_i (addr_idx),
.wdata_i (wdata_i),
.rvalid_o (read_valid),
.rdata_o (rdata_o)
);
endmodule

View file

@ -4,13 +4,6 @@
/**
* Dual-port RAM with 1 cycle read/write delay, 32 bit words.
*
* The two ports are in Read-First Mode: when reading from and writing to the same address
* simultaneously, the old content is returned, before the new content is written. New content
* is made available to both ports with one cycle delay.
*
* Simultaneous write operations by both ports to the same address are to be avoided: The data
* written to memory is not determined.
*/
`include "prim_assert.sv"
@ -40,8 +33,6 @@ module ram_2p #(
localparam int Aw = $clog2(Depth);
logic [31:0] mem [Depth];
logic [Aw-1:0] a_addr_idx;
assign a_addr_idx = a_addr_i[Aw-1+2:2];
logic [31-Aw:0] unused_a_addr_parts;
@ -52,77 +43,45 @@ module ram_2p #(
logic [31-Aw:0] unused_b_addr_parts;
assign unused_b_addr_parts = {b_addr_i[31:Aw+2], b_addr_i[1:0]};
always @(posedge clk_i) begin
if (a_req_i) begin
if (a_we_i) begin
for (int i = 0; i < 4; i = i + 1) begin
if (a_be_i[i] == 1'b1) begin
mem[a_addr_idx][i*8 +: 8] <= a_wdata_i[i*8 +: 8];
end
end
end
a_rdata_o <= mem[a_addr_idx];
end
end
always @(posedge clk_i) begin
if (b_req_i) begin
if (b_we_i) begin
for (int i = 0; i < 4; i = i + 1) begin
if (b_be_i[i] == 1'b1) begin
mem[b_addr_idx][i*8 +: 8] <= b_wdata_i[i*8 +: 8];
end
end
end
b_rdata_o <= mem[b_addr_idx];
// Convert byte mask to SRAM bit mask.
logic [31:0] a_wmask;
logic [31:0] b_wmask;
always_comb begin
for (int i = 0 ; i < 4 ; i++) begin
// mask for read data
a_wmask[8*i+:8] = {8{a_be_i[i]}};
b_wmask[8*i+:8] = {8{b_be_i[i]}};
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
a_rvalid_o <= '0;
end else begin
a_rvalid_o <= a_req_i;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
b_rvalid_o <= '0;
end else begin
a_rvalid_o <= a_req_i;
b_rvalid_o <= b_req_i;
end
end
`ifdef VERILATOR
// Task for loading 'mem' with SystemVerilog system task $readmemh()
export "DPI-C" task simutil_verilator_memload;
// Function for setting a specific 32 bit element in |mem|
// Returns 1 (true) for success, 0 (false) for errors.
export "DPI-C" function simutil_verilator_set_mem;
prim_ram_2p #(
.Width(32),
.Depth(Depth)
) u_ram (
.clk_a_i (clk_i),
.clk_b_i (clk_i),
.a_req_i (a_req_i),
.a_write_i (a_we_i),
.a_addr_i (a_addr_idx),
.a_wdata_i (a_wdata_i),
.a_wmask_i (a_wmask),
.a_rdata_o (a_rdata_o),
.b_req_i (b_req_i),
.b_write_i (b_we_i),
.b_wmask_i (b_wmask),
.b_addr_i (b_addr_idx),
.b_wdata_i (b_wdata_i),
.b_rdata_o (b_rdata_o)
);
task simutil_verilator_memload;
input string file;
$readmemh(file, mem);
endtask
// TODO: Allow 'val' to have other widths than 32 bit
function int simutil_verilator_set_mem(input int index,
input logic[31:0] val);
if (index >= Depth) begin
return 0;
end
mem[index] = val;
return 1;
endfunction
`endif
`ifdef SRAM_INIT_FILE
localparam MEM_FILE = `PRIM_STRINGIFY(`SRAM_INIT_FILE);
initial begin
$display("Initializing SRAM from %s", MEM_FILE);
$readmemh(MEM_FILE, mem);
end
`endif
endmodule

View file

@ -8,9 +8,10 @@ filesets:
files_sim_sv:
depend:
- lowrisc:prim:assert
- lowrisc:prim_generic:ram_1p
- lowrisc:prim:ram_1p
- lowrisc:prim:ram_2p
- lowrisc:prim:clock_gating
files:
- ./rtl/prim_clock_gating.sv
- ./rtl/ram_1p.sv
- ./rtl/ram_2p.sv
- ./rtl/bus.sv

View file

@ -15,7 +15,7 @@ log.basicConfig(level=log.INFO, format="%(levelname)s: %(message)s")
def get_tool_requirements_path():
'''Return the path to tool_requirements.py, at the top of the Ibex repo'''
'''Return the path to tool_requirements.py, at the top of the repo'''
# top_src_dir is the top of the repository
top_src_dir = os.path.normpath(os.path.join(os.path.dirname(__file__),
'..'))

View file

@ -17,6 +17,12 @@
{from: "hw/dv/sv/dv_utils", to: "dv_utils"},
{from: "hw/dv/verilator", to: "dv_verilator"},
{from: "hw/ip/prim", to: "prim"},
{from: "hw/ip/prim_generic", to: "prim_generic"},
{from: "hw/ip/prim_xilinx", to: "prim_xilinx"},
{from: "hw/lint", to: "lint"},
{from: "util/dvsim", to: "dvsim"},
{from: "util/uvmdvgen", to: "uvmdvgen"},
]

3
vendor/lowrisc_ip/lint/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build
reports
ascentlint.policy

133
vendor/lowrisc_ip/lint/Makefile vendored Normal file
View file

@ -0,0 +1,133 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
#
# Makefile with ascentlint, verilator-lint and verible style lint
# targets for OpenTitan
#
# TODO: currently we cannot support parallel builds since some fusesoc cores
# define filesets with files outside the current folder (e.g. using relative
# path prefixes such as "../../"). this can cause collisions between parallel
# builds since they are not nicely contained within the same folder. This should
# be solved by reworking the fusesoc core files (especially the top-level one).
CORE_ROOT ?= ../../
REPORT_DIR ?= reports
IPS ?= ip-aes \
ip-alert_handler \
ip-clkmgr \
ip-flash_ctrl \
ip-gpio \
ip-hmac \
ip-i2c \
ip-otp_ctrl \
ip-nmi_gen \
ip-padctrl \
ip-pinmux \
ip-pwrmgr \
ip-rv_core_ibex \
ip-rv_dm \
ip-rv_plic_example \
ip-rv_timer \
ip-spi_device \
ip-uart \
ip-usbdev \
ip-usb_fs_nb_pe \
ip-usbuart \
ip-rstmgr \
tlul-socket_1n \
tlul-socket_m1 \
tlul-adapter_reg \
tlul-adapter_sram \
tlul-sram2tlul \
systems-top_earlgrey
ips_lint = $(addsuffix _lint, $(IPS))
ips_vlint = $(addsuffix _vlint, $(IPS))
ips_slint = $(addsuffix _slint, $(IPS))
######################
# ascentlint targets #
######################
# lint all discovered targets and make a report
all: lint
$(MAKE) report
lint: clean
@echo Discovered lint targets:
@echo -e "\n $(patsubst %,%\\n,$(strip $(ips_lint)))"
$(MAKE) $(ips_lint)
$(ips_lint):
rm -rf build
mkdir -p ${REPORT_DIR}
-fusesoc --cores-root ${CORE_ROOT} run --target=lint --tool=ascentlint lowrisc:$(subst -,:,$(patsubst %_lint,%,$@))
cp build/lowrisc_*$(subst -,_,$(patsubst %_lint,%,$@))*/lint-ascentlint/ascentlint.log ${REPORT_DIR}/$(patsubst %_lint,%,$@).log
cp build/lowrisc_*$(subst -,_,$(patsubst %_lint,%,$@))*/lint-ascentlint/ascentlint.rpt ${REPORT_DIR}/$(patsubst %_lint,%,$@).rpt
# creates a (filtered) summary report from all available ascentlint logs/rpts
# note that lint reports have to be filtered using this script before publishing
# any information from these reports publicly
# note, the filtering script is simplistic ATM and just looks for *.rpt files
# hence we have to temporarily write the output to a file with a different extension
# since otherwise the gen_report script will try to process the summary report as well.
report:
$(eval TMPFILE=$(shell mktemp))
rm -f ${REPORT_DIR}/lint_summary.rpt
./util/gen-report.sh | tee $(TMPFILE)
cp $(TMPFILE) ${REPORT_DIR}/lint_summary.rpt
rm -f $(TMPFILE)
#####################
# verilator targets #
#####################
vall: vlint
$(MAKE) vreport
vlint: clean
@echo Discovered vlint targets:
@echo -e "\n $(patsubst %,%\\n,$(strip $(ips_vlint)))"
$(MAKE) $(ips_vlint)
$(ips_vlint):
rm -rf build
mkdir -p ${REPORT_DIR}
-fusesoc --cores-root ${CORE_ROOT} run --target=lint lowrisc:$(subst -,:,$(patsubst %_vlint,%,$@))
# TODO: add summary reporting
vreport:
##############################
# verible style lint targets #
##############################
sall: slint
$(MAKE) sreport
slint: clean
@echo Discovered vlint targets:
@echo -e "\n $(patsubst %,%\\n,$(strip $(ips_vlint)))"
$(MAKE) $(ips_vlint)
# TODO(#1727): pass Verible config file to FuseSoC, once supported by Verible
$(ips_slint):
rm -rf build
mkdir -p ${REPORT_DIR}
-fusesoc --cores-root ${CORE_ROOT} run --target=lint --tool=veriblelint lowrisc:$(subst -,:,$(patsubst %_slint,%,$@))
# TODO: add summary reporting
sreport:
##################
# common targets #
##################
clean:
rm -rf build
rm -rf ${REPORT_DIR}/*
.PHONY: all lint $(ips_lint) report vall vlint $(ips_vlint) \
vreport sall slint $(ips_slint) clean sreport

32
vendor/lowrisc_ip/lint/common.core vendored Normal file
View file

@ -0,0 +1,32 @@
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:lint:common:0.1"
description: "Common waivers"
filesets:
files_verilator:
files:
- tools/verilator/common.vlt
file_type: vlt
files_ascentlint:
files:
- tools/ascentlint/common.waiver: {file_type: waiver}
- tools/ascentlint/ascentlint-config.tcl: {file_type: tclSource}
files_veriblelint:
files:
- tools/veriblelint/rules.vbl: {file_type: veribleLintRules}
files_check_tool_requirements:
depend:
- lowrisc:tool:check_tool_requirements
targets:
default: &default_target
filesets:
- tool_verilator ? (files_verilator)
- tool_ascentlint ? (files_ascentlint)
- tool_veriblelint ? (files_veriblelint)
- files_check_tool_requirements

24
vendor/lowrisc_ip/lint/comportable.core vendored Normal file
View file

@ -0,0 +1,24 @@
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:lint:comportable:0.1"
description: "Waiver files for comportable IPs"
filesets:
files_verilator_waiver:
files:
- tools/verilator/comportable.vlt
file_type: vlt
files_ascentlint_waiver:
files:
- tools/ascentlint/comportable.waiver
file_type: waiver
targets:
default: &default_target
filesets:
- tool_verilator ? (files_verilator_waiver)
- tool_ascentlint ? (files_ascentlint_waiver)

View file

@ -0,0 +1,9 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// Ascentlint-specific results parsing script that is called after running lint
report_cmd: "{proj_root}/hw/lint/tools/{tool}/parse-lint-report.py "
report_opts: ["--repdir={build_dir}/lint-{tool}",
"--outdir={build_dir}"]
}

View file

@ -0,0 +1,33 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
flow: lint
flow_makefile: "{proj_root}/hw/lint/data/lint.mk"
import_cfgs: [// common server configuration for results upload
"{proj_root}/hw/data/common_project_cfg.hjson"
// tool-specific configuration
"{proj_root}/hw/lint/data/{tool}.hjson"]
// Name of the DUT / top-level to be run through lint
dut: "{name}"
// Default directory structure for the output
build_dir: "{scratch_path}/{build_mode}"
build_log: "{build_dir}/lint.log"
// We rely on fusesoc to run lint for us
build_cmd: "fusesoc"
build_opts: ["--cores-root {proj_root}",
"run",
"--target={flow}",
"--tool={tool}",
"--build-root={build_dir}",
"{fusesoc_core}"]
// these are not needed currently, but have to be defined
sv_flist_gen_cmd: ""
sv_flist_gen_opts: []
sv_flist_gen_dir: ""
tool_srcs: []
}

41
vendor/lowrisc_ip/lint/data/lint.mk vendored Normal file
View file

@ -0,0 +1,41 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
.DEFAULT_GOAL := all
all: build
###################
## build targets ##
###################
build: compile_result
pre_compile:
@echo "[make]: pre_compile"
mkdir -p ${build_dir} && env | sort > ${build_dir}/env_vars
mkdir -p ${tool_srcs_dir}
-cp -Ru ${tool_srcs} ${tool_srcs_dir}
compile: pre_compile
@echo "[make]: compile"
# we check the status in the parse script below
-cd ${build_dir} && ${build_cmd} ${build_opts} 2>&1 | tee ${build_log}
post_compile: compile
@echo "[make]: post_compile"
# Parse out result
compile_result: post_compile
@echo "[make]: compile_result"
${report_cmd} ${report_opts}
clean:
echo "[make]: clean"
rm -rf ${scratch_root}/${dut}/*
.PHONY: build \
run \
pre_compile \
compile \
post_compile \
compile_result

View file

@ -0,0 +1,13 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// TODO(#1342): switch over to native structured tool output, once supported by Verible
// Verible lint-specific results parsing script that is called after running lint
report_cmd: "{proj_root}/hw/lint/tools/{tool}/parse-lint-report.py "
report_opts: ["--repdir={build_dir}",
"--outdir={build_dir}"]
// This customizes the report format for style lint
is_style_lint: True
}

121
vendor/lowrisc_ip/lint/doc/README.md vendored Normal file
View file

@ -0,0 +1,121 @@
# RTL Linting
Linting is a productivity tool for designers to quickly find typos and bugs at the time when the RTL is written.
Running lint is important when using SystemVerilog, a weakly-typed language, unlike other hardware description languages.
We consider linting to be critical for conformance to our goals of high quality designs.
We have standardized on the [AscentLint](https://www.realintent.com/rtl-linting-ascent-lint/) tool from RealIntent for this task due to its fast run-times and comprehensive set of rules that provide concise error and warning messages.
The lint flow leverages a new lint rule policy named _"lowRISC Lint Rules"_ that has been tailored towards our [Verilog Style Guide](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md).
The lint flow run scripts and waiver files are available in the GitHub repository of this project, but due to the proprietary nature of the lint rules and their configuration, the _"lowRISC Lint Rules"_ lint policy file can not be publicly provided.
However, the _"lowRISC Lint Rules"_ are available as part of the default policies in AscentLint release 2019.A.p3 or newer (as `LRLR-v1.0.policy`).
This enables designers that have access to this tool to run the lint flow provided locally on their premises.
Our linting flow leverages FuseSoC to resolve dependencies, build file lists and call the linting tools. See [here](https://github.com/olofk/fusesoc) for an introduction to this opensource package manager and [here](https://docs.opentitan.org/doc/ug/install_instructions/) for installation instructions.
In order to run lint on a [comportable IP](https://docs.opentitan.org/doc/rm/comportability_specification/) block, the corresponding FuseSoC core file must have a lint target and include (optional) waiver files as shown in the following example taken from the FuseSoC core of the AES comportable IP:
```
filesets:
[...]
files_verilator_waiver:
depend:
# common waivers
- lowrisc:lint:common
- lowrisc:lint:comportable
files:
- lint/aes.vlt
file_type: vlt
files_ascentlint_waiver:
depend:
# common waivers
- lowrisc:lint:common
- lowrisc:lint:comportable
files:
- lint/aes.waiver
file_type: waiver
files_veriblelint_waiver:
depend:
# common waivers
- lowrisc:lint:common
- lowrisc:lint:comportable
[...]
targets:
default: &default_target
filesets:
- tool_verilator ? (files_verilator_waiver)
- tool_ascentlint ? (files_ascentlint_waiver)
- tool_veriblelint ? (files_veriblelint_waiver)
- files_rtl
toplevel: aes
lint:
<<: *default_target
default_tool: verilator
parameters:
- SYNTHESIS=true
tools:
verilator:
mode: lint-only
verilator_options:
- "-Wall"
```
Note that the setup shown above also supports RTL style linting with the open source tool [Verible](https://github.com/google/verible/) and RTL linting with [Verilator](https://www.veripool.org/wiki/verilator) in order to complement the sign-off lint flow with AscentLint.
In particular, Verible lint focuses on different aspects of the code, and detects style elements that are in violation with our [Verilog Style Guide](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md).
The same lint target is reused for all three tools (we override the tool selection when invoking FuseSoC).
Lint waivers can be added to the flow by placing them in the corresponding waiver file.
In this example this would be `lint/aes.waiver` for AscentLint and `lint/aes.vlt` for Verilator.
In order to manually run lint on a specific block, make sure AscentLint is properly installed and step into the `hw/lint` folder.
The makefile in that folder contains all targets that can be manually invoked.
For example, to run lint on AES, do:
```
make ip-aes_lint
```
This run will exit with PASSED status on the command line if there are no lint errors or warnings.
Otherwise it will exit with ERROR status, in which case you can get more information by running
```
make clean
make ip-aes_lint
make report
```
In order to build all lint targets and produce a summary report, the `make all` target can be invoked.
For more detailed information on a particular lint run you can inspect the tool output inside the build folder that is created by FuseSoC.
Note that all AscentLint targets have a Verilator and Verible counterparts that are suffixed with `_vlint` and `_slint`, respectively.
This enables designers without access to AscentLint to iterate with open-source tools before making their first Pull Request.
For batch regressions we have integrated this flow into the `dvsim` tool, which can be invoked as follows from the root of the project repository:
```
util/dvsim.py hw/top_earlgrey/lint/ascentlint/top_earlgrey_lint_cfgs.hjson --tool "ascentlint" --purge -mp 1
```
where the `top_earlgrey_lint_cfgs.hjson` file contains all the lint targets to be run in that regression (currently all available comportable IPs and the top-level are run).
The `purge` option ensures that the scratch directory is fully erased before starting the build, and `mp 1` sets the number of parallel workers to one (should be set depending on your licensing situation).
The batch regression is regularly run on the master branch at eight-hour intervals, and the results are published on a public dashboard such that everybody can inspect the current lint status of all IPs on the project website.
The dashboard can be found by following the appropriate link on the [hardware IP overview page](https://docs.opentitan.org/hw).
# CDC Linting
Logic designs that have signals that cross from one clock domain to
another unrelated clock domain are notorious for introducing hard to
debug problems. The reason is that design verification, with its constant
and idealized timing relationships on signals, does not represent the
variability and uncertainty of real world systems. For this reason,
maintaining a robust Clock Domain Crossing verification strategy ("CDC
methodology") is critical to the success of any multi-clock design.
Currently, due to the proprietary nature of tool collateral, all CDC linting
activity is done offline and reported back to designers. The project will
standardize on a particular CDC linting tool, and results will be shared in
some form through continuous integration build results, published tool
outputs, pre-submit checks, and/or linting summaries of tool output
(TODO: publication details). At that time this README will be updated
with setup and run instructions.
This holds for *Reset Domain Crossing* ("RDC") methodology as well.

View file

@ -0,0 +1,9 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
# get installation path of ascentlint
set RI_INSTALL [file dirname [exec which ascentlint]]
# source the policy file containing the lowrisc lint rules
source "$RI_INSTALL/../Ascent/Lint/lib/policies/lowRISC/LRLR-v1.0.policy"

View file

@ -0,0 +1,11 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
#
# common waiver rules for ascentlint
# waiver for unused_* signals for HIER_* rules (note that our policy file has a
# similar exception list for rule NOT_READ)
waive -rules {HIER_NET_NOT_READ HIER_BRANCH_NOT_READ} -pattern {unused_*}
waive -rules {HIER_NET_NOT_READ HIER_BRANCH_NOT_READ} -pattern {gen_*.unused_*}

View file

@ -0,0 +1,21 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
#
# comportable IP waiver rules for ascentlint
# auto-generated register files
waive -rules CONST_FF -location {*_reg_top*} -regexp {rsp_opcode.*is driven by constant zeros} \
-comment "makes the code more readable"
waive -rules CONST_OUTPUT -location {*_reg_top*} -regexp {Output 'tl_o.d_(param|size|sink|user)' is driven by constant} \
-comment "makes the code more readable"
waive -rules INPUT_NOT_READ -location {*_reg_top*} -regexp {Input port.*a_(address|param|user).*not read from} \
-comment "several TLUL signals are not used by register file"
waive -rules HIER_NET_NOT_READ -location {*_reg_top*} -regexp {Net 'tl_reg_h2d.a_(address|param|user).* is not read from} \
-comment "several TLUL signals are not used by register file"
waive -rules CASE_SEL_CONST -location {*_reg_top*} \
-comment "addr_hit is one hot encoded."
waive -rules LINE_LENGTH -location {*_reg_top*} -regexp {Line length of .* exceeds .* character limit} \
-comment "These files are one-liners in order to comply with our SV style guide."

View file

@ -0,0 +1,125 @@
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r"""Parses lint report and dump filtered messages in hjson format.
"""
import argparse
import re
import sys
from pathlib import Path
import hjson
def extract_messages(full_file, patterns, results):
"""
This extracts messages from the sting buffer full_file.
The argument patterns needs to be a list of tuples with
(<error_severity>, <pattern_to_match_for>).
"""
for severity, pattern in patterns:
results[severity] += re.findall(pattern, full_file, flags=re.MULTILINE)
return results
def get_results(resdir):
"""
Parse report and corresponding logfiles and extract error, warning
and info messages for each IP present in the result folder
"""
results = {
"tool": "ascentlint",
"errors": [],
"warnings": [],
"lint_errors": [],
"lint_warnings": [],
"lint_infos": []
}
try:
# check the log file for flow errors and warnings
with Path(resdir).joinpath('ascentlint.log').open() as f:
full_file = f.read()
err_warn_patterns = [("errors", r"^FlexNet Licensing error.*"),
("errors", r"^Error: .*"),
("errors", r"^ ERR .*"),
("warnings", r"^Warning: .*"),
("warnings", r"^ WARN .*")]
extract_messages(full_file, err_warn_patterns, results)
except IOError as err:
results["errors"] += ["IOError: %s" % err]
try:
# check the report file for lint INFO, WARNING and ERRORs
with Path(resdir).joinpath('ascentlint.rpt').open() as f:
full_file = f.read()
err_warn_patterns = {("lint_errors", r"^E .*"),
("lint_warnings", r"^W .*"),
("lint_infos", r"^I .*")}
extract_messages(full_file, err_warn_patterns, results)
except IOError as err:
results["errors"] += ["IOError: %s" % err]
return results
def main():
parser = argparse.ArgumentParser(
description="""This script parses AscentLint log and report files from
a lint run, filters the messages and creates an aggregated result
.hjson file with the following fields:
{"tool": "ascentlint",
"errors" : [],
"warnings" : [],
"lint_errors" : [],
"lint_warnings" : [],
"lint_infos" : []}
The fields 'errors' and 'warnings' contain file IO messages or
messages output by the tool itself, whereas the fields prefixed with
'lint_' contain lint-related messages.
The script returns nonzero status if any warnings or errors are present.
""")
parser.add_argument('--repdir',
type=str,
default="./",
help="""The script searches the 'ascentlint.log' and
'ascentlint.rpt' files in this directory.
Defaults to './'""")
parser.add_argument('--outdir',
type=str,
default="./",
help="""Output directory for the 'results.hjson' file.
Defaults to './'""")
args = parser.parse_args()
results = get_results(args.repdir)
with Path(args.outdir).joinpath("results.hjson").open("w") as results_file:
hjson.dump(results,
results_file,
ensure_ascii=False,
for_json=True,
use_decimal=True)
# return nonzero status if any warnings or errors are present
# lint infos do not count as failures
nr_errors = len(results["errors"]) + len(results["lint_errors"])
nr_warnings = len(results["warnings"]) + len(results["lint_warnings"])
if nr_errors > 0 and nr_warnings > 0:
print("Lint not successful, got %d warnings and %d errors." %
(nr_warnings, nr_errors))
sys.exit(1)
print("Lint successful, got %d warnings and %d errors." %
(nr_warnings, nr_errors))
sys.exit(0)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,127 @@
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r"""Parses lint report and dump filtered messages in hjson format.
"""
import argparse
import re
import sys
from pathlib import Path
import hjson
def extract_messages(full_file, patterns, results):
"""
This extracts messages from the sting buffer full_file.
The argument patterns needs to be a list of tuples with
(<error_severity>, <pattern_to_match_for>).
"""
for severity, pattern in patterns:
results[severity] += re.findall(pattern, full_file, flags=re.MULTILINE)
return results
def get_results(resdir):
"""
Parse report and corresponding logfiles and extract error, warning
and info messages for each IP present in the result folder
"""
results = {
"tool": "veriblelint",
"errors": [],
"warnings": [],
"lint_errors": [],
"lint_warnings": [],
"lint_infos": []
}
try:
# check the report file for lint INFO, WARNING and ERRORs
with Path(resdir).joinpath('lint.log').open() as f:
full_file = f.read()
err_warn_patterns = {
# The lint failed error can be ignored, since
# Fusesoc will always return this error if lint warnings have
# been found. We have a way of capturing the lint warnings
# explicitly in this parsing script, hence this error is redundant
# and we decided not to report it in the dashboard.
("errors",
r"^(?!ERROR: Failed to run .* Lint failed)ERROR: .*"),
("errors", r"^Error: .*"),
("errors", r"^E .*"),
# TODO(https://github.com/olofk/edalize/issues/90):
# this is a workaround until we actually have native Edalize
# support for JasperGold and "formal" targets
("warnings",
r"^(?!WARNING: Unknown item formal in section Target)WARNING: .*"
),
("warnings", r"^Warning: .* "),
("warnings", r"^W .*"),
("lint_warnings", r"^.*\[Style:.*")
}
extract_messages(full_file, err_warn_patterns, results)
except IOError as err:
results["errors"] += ["IOError: %s" % err]
return results
def main():
parser = argparse.ArgumentParser(
description="""This script parses verible lint log files from
a lint run, filters the messages and creates an aggregated result
.hjson file with the following fields:
{"tool": "veriblelint",
"errors" : [],
"warnings" : [],
"lint_errors" : [],
"lint_warnings" : [],
"lint_infos" : []}
The fields 'errors' and 'warnings' contain file IO messages or
messages output by the tool itself, whereas the fields prefixed with
'lint_' contain lint-related messages.
The script returns nonzero status if any warnings or errors are present.
""")
parser.add_argument('--repdir',
type=str,
default="./",
help="""The script searches the 'lint.log'
files in this directory.
Defaults to './'""")
parser.add_argument('--outdir',
type=str,
default="./",
help="""
Output directory for the 'results.hjson' file.
Defaults to '%(default)s'""")
args = parser.parse_args()
results = get_results(args.repdir)
with Path(args.outdir).joinpath("results.hjson").open("w") as results_file:
hjson.dump(results,
results_file,
ensure_ascii=False,
for_json=True,
use_decimal=True)
# return nonzero status if any warnings or errors are present
# lint infos do not count as failures
nr_errors = len(results["errors"]) + len(results["lint_errors"])
nr_warnings = len(results["warnings"]) + len(results["lint_warnings"])
print("Lint not successful, got %d warnings and %d errors." %
(nr_warnings, nr_errors))
if nr_errors > 0 and nr_warnings > 0:
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,19 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
#
# OpenTitan-specific style lint rule configurations
# line length currently set to 150 to remove clutter in reports.
# set this back to 100 once we can waive this rule for generated
# files or once the file generators can respect this constraint
line-length=length:150
# we allow "classic" verilog string parameters without explicit type
explicit-parameter-storage-type=exempt_type:string
# localparam can be both ALL_CAPS and CamelCase according to our style
parameter-name-style=localparam_style:CamelCase|ALL_CAPS
# we allow nested struct definitions
-typedef-structs-unions

View file

@ -0,0 +1,6 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// common waiver rules for verilator

View file

@ -0,0 +1,6 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// comportable IP waiver rules for verilator

54
vendor/lowrisc_ip/lint/util/gen-report.sh vendored Executable file
View file

@ -0,0 +1,54 @@
#!/bin/bash
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
#
# Ascentlint report summary generation script.
#
REPORT_DIR=reports
#-------------------------------------------------------------------------
# print header
#-------------------------------------------------------------------------
printf "NUMBER OF LINT ERRORS PER BLOCK:\n\n"
format="%20s %10s %10s \n"
printf "${format}" "Block" "Errors" "Warnings"
echo "-------------------------------------------"
#-------------------------------------------------------------------------
# run lint and summarize results
#-------------------------------------------------------------------------
for report in ${REPORT_DIR}/*.rpt ; do
# summarize results
crash=`grep "Exiting with error status" "${report%.*}.log"`
if [[ ! -z "$crash" ]]; then
error_cnt="CRASH"
warni_cnt="CRASH"
else
error_cnt=`grep "^E " "${report%.*}.rpt" | wc -l`
warni_cnt=`grep "^W " "${report%.*}.rpt" | wc -l`
fi
printf "${format}" `basename "${report%.*}"` $error_cnt $warni_cnt
done
echo "-------------------------------------------"
echo "END SUMMARY"
#-------------------------------------------------------------------------
# generate detailed reports
#-------------------------------------------------------------------------
printf "\n\nLIST OF ERRORS (E) AND WARNINGS (W) FOR EACH BLOCK:"
for report in ${REPORT_DIR}/*.rpt ; do
printf "\n\n`basename "${report%.*}"`\n"
# grep for lint crashes and lint errors, and limit line length
grep "^ ERR" -A 2 "${report%.*}.log" | cut -c -200
grep "^E " "${report%.*}.rpt" | cut -c -200
grep "^W " "${report%.*}.rpt" | cut -c -200
done

199
vendor/lowrisc_ip/prim/README.md vendored Normal file
View file

@ -0,0 +1,199 @@
# lowRISC Hardware Primitives
## Concepts
This directory contains basic building blocks to create a hardware design,
called primitives. A primitive is described by its name, and has a well-defined
list of ports and parameters.
Under the hood, primitives are slightly special, as they can have multiple
implementations. In contrast to many other modules in a hardware design,
primitives must often be implemented in technology-dependent ways. For example,
a clock multiplexer for a Xilinx FPGA is implemented differently than one for
a specific ASIC technology.
Not all primitives need to have multiple implementations.
* Primitives with a single, generic, implementation are normal SystemVerilog
modules inside the `hw/ip/prim/rtl` directory. We call these primitives
"technology-independent primitives".
* Primitives with multiple implementations have only a FuseSoC core file in the
`hw/ip/prim` directory. The actual implementations are in "technology
libraries". We call these primitives "technology-dependent primitives".
### Abstract primitives
Abstract primitives are wrappers around technology-dependent implementations of
primitives, with the ability to select a specific implementation if needed.
In more technical terms, abstract primitives are SystemVerilog modules. The
example below shows one.
```systemverilog
`ifndef PRIM_DEFAULT_IMPL
`define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric
`endif
module prim_pad_wrapper
#(
parameter int unsigned AttrDw = 6
) (
inout wire inout_io, // bidirectional pad
output logic in_o, // input data
input out_i, // output data
input oe_i, // output enable
// additional attributes {drive strength, keeper, pull-up, pull-down, open-drain, invert}
input [AttrDw-1:0] attr_i
);
parameter prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL;
if (Impl == prim_pkg::ImplGeneric) begin : gen_generic
prim_generic_pad_wrapper u_impl_generic (
.*
);
end else if (Impl == prim_pkg::ImplXilinx) begin : gen_xilinx
prim_xilinx_pad_wrapper u_impl_xilinx (
.*
);
end else begin : gen_failure
// TODO: Find code that works across tools and causes a compile failure
end
endmodule
```
As seen from the source code snippet, abstract primitives have the following
properties:
- They have an `Impl` parameter which can be set to choose a specific
implementation of the primitive.
- The `Impl` parameter is set to a system-wide default determined by the
`PRIM_DEFAULT_IMPL` define.
- All ports and parameters of the abstract primitive are forwarded to the
implementations.
### Technology libraries
Technology libraries collect implementations of primitives.
At least one technology library must exist: the `generic` technology library,
which contains a pure-SystemVerilog implementation of the functionality. This
library is commonly used for simulations and as functional reference. The
`generic` technology library is contained in the `hw/ip/prim_generic` directory.
In addition to the implementation in the `generic` library, primitives may be
implemented by as many other libraries as needed.
Technology libraries are referenced by their name.
### Technology library discovery
In many cases, technology libraries contain vendor-specific code which cannot be
shared widely or openly. Therefore, a FuseSoC looks for available technology
libraries at build time, and makes all libraries it finds available.
The discovery is performed based on the agreed-on naming scheme for primitives.
- FuseSoC scans all libraries (e.g. as specified by its `--cores-root` command
line argument) for cores.
- All cores with a name matching `lowrisc:prim_TECHLIBNAME:PRIMNAME`
are considered. `TECHLIBNAME` is then added to the list of technology
libraries.
After the discovery process has completed, a script (`primgen`) creates
- an abstract primitive (see above), and
- an entry in the `prim_pkg` package in the form of `prim_pkg::ImplTechlibname`
to identify the technology library by its name.
## User Guide
### Use primitives
Primitives are normal SystemVerilog modules, and can be used as usual:
* instantiate it like a normal SystemVerilog module, and
* add a dependency in the FuseSoC core file.
Technology-dependent primitives have an additional parameter called `Impl`.
Set this parameter to use a specific implementation of the primitive for this
specific instance. For example:
```systemverilog
prim_ram_2p #(
.Width (TotalWidth),
.Depth (Depth),
// Force the use of the tsmc40lp technology library for this instance, instead
// of using the build-time default.
.Impl(prim_pkg::ImplTsmc40lp)
) u_mem (
.clk_a_i (clk_i),
...
)
```
### Set the default technology library
If no specific technology library is chosen for an instantiated primitive the
default library is used. The SystemVerilog define `PRIM_DEFAULT_IMPL` can be
used to set the default for the whole design. Set this define to one of the enum
values in `prim_pkg.sv` in the form `prim_pkg::ImplTechlibname`. `Techlibname`
is the capitalized name of the technology library.
In the top-level FuseSoC core file the default technology library can be chosen
like this:
```yaml
# my_toplevel.core
# Declare filesets and other things (omitted)
parameters:
# Make the parameter known to FuseSoC to enable overrides from the
# command line. If not overwritten, use the generic technology library.
PRIM_DEFAULT_IMPL:
datatype: str
paramtype: vlogdefine
description: Primitives implementation to use, e.g. "prim_pkg::ImplGeneric".
default: prim_pkg::ImplGeneric
targets:
fpga_synthesis:
filesets:
- my_rtl_files
parameters:
# Use the xilinx technology library for this target by default.
- PRIM_DEFAULT_IMPL=prim_pkg::ImplXilinx
toplevel: my_toplevel
```
### Create a technology library
To create a technology library follow these steps:
- Choose a name for the new technology library. Names are all lower-case.
To ease sharing of technology libraries it is encouraged to pick a very
specific name, e.g. `tsmc40lp`, and not `asic`.
- Copy the `prim_generic` folder into an arbitrary location (can be outside
of this repository). Name the folder `prim_YOURLIBRARYNAME`.
- Replace the word `generic` everywhere with the name of your technology
library. This includes
- file and directory names (e.g. `prim_generic_ram1p.sv` becomes
`prim_tsmc40lp_ram1p.sv`),
- module names (e.g. `prim_generic_ram1p` becomes `prim_tsmc40lp_ram1p`), and
- all other references (grep for it!).
- Implement all primitives. Replace the module body of the generic
implementation with a technology-specific implementation as needed. Do *not*
modify the list of ports or parameters in any way!
## Implementation details
Technology-dependent primitives are implemented as a FuseSoC generator. The
core of the primitive (e.g. `lowrisc:prim:rom` in `prim/prim_rom.core`) calls
a FuseSoC generator. This generator is the script `util/primgen.py`. As input,
the script receives a list of all cores found by FuseSoC anywhere in its search
path. The script then looks through the cores FuseSoC discovered and extracts
a list of technology libraries out of it. It then goes on to create the
abstract primitive (copying over the list of parameters and ports from the
generic implementation), and an associated core file, which depends on all
technology-dependent libraries that were found.

View file

@ -0,0 +1,89 @@
---
title: "Primitive Component: Keccak permutation"
---
# Overview
`prim_keccak` is a single round implementation of the permutation stage in [SHA3 algorithm][fibs-pub-202].
Keccak primitive module assumes the number of rounds is less than or equal to 12 + 2L.
It supports all combinations of the data width described in the [spec][fibs-pub-202].
This implementation is not currently hardened against side-channel or fault injection attacks.
It implements the Keccak_p function.
[fibs-pub-202]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
## Parameters
Name | Type | Description
------|------|----------------------------------------------------------------
Width | int | state width in bits. can be 25, 50, 100, 200, 400, 800, or 1600
### Derived Parameters
The parameters below are derived parameter from `Width` parameter.
Name | Type | Description
---------|------|-------------------------------------------------------
W | int | number of slices in state. `Width/25`
L | int | log2 of `W`
MaxRound | int | maximum allowed round value. `12 + 2L`
RndW | int | bit-width to represent MaxRound. log2 of `MaxRound`
## Signal Interfaces
Signal | Type | Description
-------|---------------|------------------------------
rnd_i | input [RndW] | current round number [0..(MaxRound-1)]
s_i | input [Width] | state input
s_o | output[Width] | permutated state output
`s_i` and `s_o` are little-endian bitarrays.
The [SHA3 spec][fibs-pub-202] shows how to convert the bitstream into the 5x5xW state cube.
For instance, bit 0 of the stream maps to `A[0,0,0]`.
The bit 0 in the spec is the first bit of the bitstream.
In `prim_keccak`, `s_i[0]` is the first bit and `s_i[Width-1]` is the last bit.
# Theory of Operations
```
| |
rnd_i | |
---/---->| -----------------------------------------\ |
[RndW] | | |
| | |
s_i | V | s_o
===/====>| bit2s() -> chi(pi(rho(theta))) -> iota( ,rnd) -> s2bit() |==/==>
[Width] | |-----------keccak_p--------------| |[Width]
| |
```
`prim_keccak` implements "Step Mappings" section in [SHA3 spec][fibs-pub-202].
It is composed of five unique permutation functions, theta, rho, pi, chi, and iota.
Also it has functions that converts bitstream of `Width` into `5x5xW` state and vice versa.
Three constant parameters are defined inside the keccak primitive module.
The rotate position described in phi function is hard-coded as below.
The value is described in the SHA3 specification.
```systemverilog
localparam int PiRotate [5][5] = '{
//y 0 1 2 3 4 x
'{ 0, 3, 1, 4, 2},// 0
'{ 1, 4, 2, 0, 3},// 1
'{ 2, 0, 3, 1, 4},// 2
'{ 3, 1, 4, 2, 0},// 3
'{ 4, 2, 0, 3, 1} // 4
};
```
The shift amount in rho function is defined as `RhoOffset` parameter.
The value is same as in the specification, but it is used as `RhoOffset % W`.
For instance, `RhoOffset[2][2]` is 171.
If `Width` is 1600, the value used in the design is `171%64`, which is `43`.
The round constant is calculated by the tool `hw/ip/prim/util/keccak_rc.py`.
The recommended default value of 24 rounds is used in this design,
but an argument (changed with the `-r` flag) is provided for reference.
The `keccak_rc.py` script creates 64 bit of constants and the `prim_keccak` module uses only lower bits of the constants if the `Width` is less than 1600.
For instance, if `Width` is 800, lower 32bits of the round constant are used.

91
vendor/lowrisc_ip/prim/doc/prim_lfsr.md vendored Normal file
View file

@ -0,0 +1,91 @@
---
title: "Primitive Component: LFSR"
---
# Overview
`prim_lfsr` is a parameterized linear feedback shift register (LFSR)
implementation that supports Galois (XOR form) and Fibonacci (XNOR form)
polynomials. The main difference between Galois and Fibonacci is that the
former has a shorter critical timing path since the XOR Gates are interleaved
with the shift register, whereas the latter combines several shift register taps
and reduces them with an XNOR tree. For more information, refer to
[this page](https://en.wikipedia.org/wiki/Linear-feedback_shift_register). Both
LFSR flavors have maximal period (`2^LfsrDw - 1`). The recommendation is to use
the Galois type and fall back to the Fibonacci type depending on the polynomial
width availability in the lookup table (see below).
## Parameters
Name | type | Description
-------------|--------|----------------------------------------------------------
LfsrType | string | LFSR form, can be `"GAL_XOR"` or `"FIB_XNOR"`
LfsrDw | int | Width of the LFSR
EntropyDw | int | Width of the entropy input
StateOutDw | int | Width of the LFSR state to be output (`lfsr_q[StateOutDw-1:0]`)
DefaultSeed | logic | Initial state of the LFSR, must be nonzero for XOR and non-all-ones for XNOR forms.
CustomCoeffs | logic | Custom polynomial coefficients of length LfsrDw.
MaxLenSVA | bit | Enables maximum length assertions, use only in sim and FPV.
## Signal Interfaces
Name | In/Out | Description
---------------------|--------|---------------------------------
seed_en_i | input | External seed input enable
seed_i[LfsrDw] | input | External seed input
lfsr_en_i | input | Lfsr enable
entropy_i[EntropyDw] | input | Entropy input
state_o[StateOutDw] | output | LFSR state output.
# Theory of Operations
```
/----------------\
seed_en_i | |
------------>| lfsr |
seed_i | |
=====/======>| LfsrDw |
[LfsrDw] | LfsrType |
lfsr_en_i | EntropyDw |
------------>| StateOutDw |
entropy_i | DefaultSeed | state_o
=====/======>| CustomCoeffs |=====/=======>
[EntropyDw] | MaxLenSVA | [StateOutDw]
| |
\----------------/
```
The LFSR module has an enable input and an additional entropy input that is
XOR'ed into the LFSR state (connect to zero if this feature is unused). The
state output contains the lower bits of the LFSR state from `StateOutDw-1`
downto `0`. As the entropy input may cause the LFSR to jump into its parasitic
state (all-zero for XOR, all-ones for XNOR), the LFSR state transition function
contains a lockup protection which re-seeds the state with `DefaultSeed` once
this condition is detected.
The LFSR contains an external seed input `seed_i` which can be used to load a
custom seed into the LFSR by asserting `seed_en_i`. This operation takes
precedence over internal state updates. If the external seed happens to be a
parasitic state, the lockup protection feature explained above will reseed the
LFSR with the `DefaultSeed` in the next cycle.
The LFSR coefficients are taken from an internal set of lookup tables with
precomputed coefficients. Alternatively, a custom polynomial can be provided
using the `Custom` parameter. The lookup tables contain polynomials for both
LFSR forms and range from 4bit to 64bit for the Galois form and 3bit to 168bit
for the Fibonacci form. The polynomial coefficients have been obtained from
[this page](https://users.ece.cmu.edu/~koopman/lfsr/) and
[Xilinx application note 52](https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf).
The script `./script/get-lfsr-coeffs.py` can be used to download, parse and dump
these coefficients in SV format as follows:
```
$ script/get-lfsr-coeffs.py -o <output_file>
```
The default is to get the Galois coefficients. If the Fibonacci coefficients
are needed, add the `--fib` switch to the above command.
The implementation of the state transition function of both polynomials have
been formally verified. Further, all polynomials up to 34bit in length have been
swept through in simulation in order to ensure that they are of
maximal-length.

View file

@ -0,0 +1,106 @@
---
title: "Primitive Component: Packer"
---
# Overview
`prim_packer` is a module that receives partial writes then packs and creates
full configurable width writes. It is one of a set of shared primitive modules
available for use within OpenTitan as referred to in the Comportability
Specification section on shared primitives.
## Parameters
Name | type | Description
-----|------|-------------
InW | int | Input data width
OutW | int | Output data width
## Signal Interfaces
Name | In/Out | Description
-------------|--------|-------------
valid_i | input | Input data available.
data_i[InW] | input | Input data.
mask_i[InW] | input | Input bit mask. Ones in the mask must be contiguous.
ready_o | output | Indicates if prim_packer is able to accept data.
valid_o | output | Indicates if output data is available.
data_o[OutW] | output | Output data.
mask_o[OutW] | output | Output bit mask.
ready_i | input | Output data can be drained.
flush_i | input | Send out stored data and clear state.
flush_done_o | output | Indicates flush operation is completed.
# Theory of Opeations
```code
/----------\
valid_i | | valid_o
---------->| |--------------->
data_i | stacked | data_o
=====/====>| register |=======/=======>
[InW] | | [OutW]
mask_i | | mask_o
=====/====>| InW+OutW |=======/=======>
ready_o |----------| ready_i
<----------| |<---------------
| |
\----------/
```
`prim_packer` accepts `InW` bits of data and bitmask signals. On a `valid_i`/
`ready_o` handshake, `data_i` is stored to internal registers and accumulated
until `OutW` data has been gathered. In the normal case, `mask_o` will be a
full width write (`{OutW{1'b1}}`). However, when `flush_i` is asserted,
`prim_packer` attempts to drain out all remaining data in the internal
storage. In this case, `mask_o` might be partial.
The internal register size is `InW + OutW` bits to safely store the incoming
data and send outgoing data to the `data_o` port.
{{< wavejson >}}
{ signal: [
{ name: 'valid_i', wave: '01.01......0.'},
{ name: 'data_i[3:0]', wave: 'x==x===.===x.', data:'0h 1h 2h 3h 4h 5h 6h 7h'},
{ name: 'mask_i[3:0]', wave: 'x==x===.===x.', data:'Fh Fh Fh Fh Fh Fh Ch Ch'},
{ name: 'ready_o', wave: '1.....01.....'},
{ name: 'valid_o', wave: '0.10101..0.10'},
{ name: 'data_o[5:0]', wave: 'x.=x=x=.=x.=x', data:'10h 08h 03h 15h 05h'},
{ name: 'mask_o[5:0]', wave: 'x.=x=x=.=x.=x', data:'3Fh 3Fh 3Fh 3Fh 0Fh '},
{ name: 'ready_i', wave: '1.....01.....'},
{ name: 'flush_i', wave: '0..........10'},
{ name: 'flush_done_o', wave: '0..........10'},
],
head:{
text: 'prim_packer',
tick: ['0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ']
}
}
{{< /wavejson >}}
The above waveform shows the case of InW := 4 and OutW := 6. After the first
transaction, `prim_packer` has `0h` in the storage. When the second `valid_i`
is asserted, it combines `0h` and incoming data `1h` and creates output `10h`
(`6'b01_0000`). The remaining `2'b00` is put into the internal storage from
`data_i[3:2]`. The next transaction combines this and input data `2h` to create
`6'b00_1000`.
`prim_packer` deasserts `ready_o` to indicate it cannot accept further data.
`ready_o` is deasserted when `ready_i` is deasserted and there is insufficient
internal storage available to store incoming data, as shown in cycle 6 above.
At cycle 9 and 10, `mask_i` is used to only load 2 bits of data into the packer
each cycle. This is to show how the packer allows misaligned writes (smaller
than `InW`) to be packed together.
At the end of the sequence, `flush_i` is asserted, and the remaining data is
drained. In this case, `mask_o` isn't full to indicate only partial data is
available (`6'b00_1111`). `flush_done_o` is asserted as soon as the remaining
data is drained.
`prim_packer` only supports packing to the right. To use `prim_packer` in a
design requiring packing to the left (filling MSB first), the design needs to
reverse the bit order (and in some cases, the byte order) before pushing to the
packer, then reverse the data output.

View file

@ -0,0 +1,70 @@
---
title: "Primitive Component: PRESENT Scrambler"
---
# Overview
`prim_present` is an (unhardened) implementation of the encryption pass of the [64bit PRESENT block cipher](https://en.wikipedia.org/wiki/PRESENT).
It is a fully unrolled combinational implementation that supports both key lengths specified in the paper (80bit and 128bit).
Further, the number of rounds is fully configurable, and the primitive supports a 32bit block cipher flavor which is not specified in the original paper.
It should be noted, however, that reduced-round and/or 32bit versions **are not secure** and must not be used in a setting where cryptographic cipher strength is required.
I.e., this primitive is only intended to be used as a lightweight data scrambling device.
## Parameters
Name | type | Description
-------------|--------|----------------------------------------------------------
DataWidth | int | Block size, can be 32 or 64
KeyWidth | int | Key size, can be 64, 80 or 128
NumRounds | int | Number of PRESENT rounds, has to be greater than 0
## Signal Interfaces
Name | In/Out | Description
-------------|--------|---------------------------------
data_i | input | Plaintext input
key_i | input | Key input
data_o | output | Output of the ciphertext
# Theory of Operations
```
/---------------\
| |
| PRESENT |
key_i | |
=====/======>| DataWidth |
[KeyWidth] | KeyWidth |
| NumRounds |
data_i | | data_o
=====/======>| |=====/=======>
[DataWidth] | | [DataWidth]
| |
\---------------/
```
The PRESENT module is fully unrolled and combinational, meaning that it does not have any clock, reset or handshaking inputs.
The only inputs are the key and the plaintext, and the only output is the ciphertext.
The internal construction follows the the algorithm described in the original [paper](http://www.lightweightcrypto.org/present/present_ches2007.pdf).
The block size is 64bit and the key size can be either 80bit or 128bit, depending on the security requirements.
In its original formulation, this cipher has 31 rounds comprised of an XOR operation with a round key, followed by the application of an s-box and a permutation layer:
```c++
round_keys = key_derivation(key_i);
state = data_i;
for (int i=1; i < 32; i++) {
state = state ^ round_keys[i];
state = sbox4_layer(state);
state = perm_layer(state);
}
data_o = state ^ round_keys[32];
```
The reduced 32bit block-size variant implemented is non-standard and should only be used for scrambling purposes, since it **is not secure**.
It leverages the same crypto primitives and key derivation functions as the 64bit variant, with the difference that the permutation layer is formulated for 32 instead of 64 elements.

View file

@ -0,0 +1,112 @@
---
title: "Primitive Component: PRINCE Scrambler"
---
# Overview
`prim_prince` is an (unhardened) implementation of the [64bit PRINCE block cipher](https://en.wikipedia.org/wiki/Prince_(cipher)).
It is a fully unrolled combinational implementation with a configurable number of rounds.
Due to the mirrored construction of this cipher, the same circuit can be used for encryption and decryption, as described below.
Further, the primitive supports a 32bit block cipher flavor which is not specified in the original paper.
It should be noted, however, that reduced-round and/or 32bit versions **are not secure** and must not be used in a setting where cryptographic cipher strength is required.
I.e., this primitive is only intended to be used as a lightweight data scrambling device.
This [paper](https://csrc.nist.gov/csrc/media/events/lightweight-cryptography-workshop-2015/documents/papers/session7-maene-paper.pdf) compares several lightweight ciphers, where PRINCE has been found to be the fastest candidate with the lowest circuit complexity among the algorithms compared.
## Parameters
Name | type | Description
---------------|--------|----------------------------------------------------------
DataWidth | int | Block size, can be 32 or 64.
KeyWidth | int | Key size, can be 64 for block size 32, or 128 for block size 64
NumRounds | int | Half the number of the reflected PRINCE rounds. Can range from 1 to 5. The effective number of non-linear layers is 2 + 2 * NumRounds.
UseOldKeySched | bit | If set to 1, fall back to the original keyschedule (not recommended). Defaults to 0.
## Signal Interfaces
Name | In/Out | Description
-------------|--------|---------------------------------
data_i | input | Plaintext input
key_i | input | Key input
dec_i | input | Assert for decryption
data_o | output | Output of the ciphertext
# Theory of Operations
```
/----------------\
dec_i | |
------------>| PRINCE |
key_i | |
=====/======>| DataWidth |
[KeyWidth] | KeyWidth |
| NumRounds |
data_i | UseOldKeySched | data_o
=====/======>| |=====/=======>
[DataWidth] | | [DataWidth]
| |
\----------------/
```
The PRINCE module is fully unrolled and combinational, meaning that it does not have any clock, reset or handshaking inputs.
The only inputs are the key and the plaintext, and the only output is the ciphertext.
The internal construction follows the the algorithm described in the original [paper](https://eprint.iacr.org/2012/529.pdf).
The block size is 64bit and the key size is 128bit.
In its original formulation, this cipher has 11 rounds (but 12 non-linear layers), which are arranged in a mirrored structure, which allows the same circuit to be used for encryption and decryption with a lightweight tweak applied to the key:
```c++
k0, k0_prime, k1 = key_derivation(key_i, dec_i);
// decryption mode
if (dec_i) {
swap(k0, k0_prime);
k1 ^= ALPHA_CONSTANT;
}
state = data_i ^ k0;
state ^= k1;
state ^= ROUND_CONSTANT[0];
// forward pass
for (int i=1; i < 6; i++) {
state = sbox4_layer(state);
state = mult_layer(state);
state = shiftrows_layer(state);
state ^= ROUND_CONSTANT[i]
data_state ^= (k & 0x1) ? k0 : k1;
}
// middle part
state = sbox4_layer(state);
state = mult_layer(state);
state = sbox4_inverse_layer(state);
// reverse pass
for (int i=6; i < 11; i++) {
data_state ^= (k & 0x1) ? k1 : k0;
state ^= ROUND_CONSTANT[i]
state = shiftrows_inverse_layer(state);
state = mult_layer(state);
state = sbox4_inverse_layer(state);
}
state ^= ROUND_CONSTANT[11];
state ^= k1;
data_o = state ^ k0_prime;
```
The multiplicative layer is an involution, meaning that it is its own inverse and it can hence be used in the reverse pass without inversion.
It should be noted that the actual choice of the `ALPHA_CONSTANT` used in the key tweak can have security impacts as detailed in [this paper](https://eprint.iacr.org/2015/372.pdf).
The constant chosen by the designers of PRINCE does not have these issues - but proper care should be taken if it is decided to modify this constant.
Also, [this paper](https://eprint.iacr.org/2014/656.pdf) proposes an improved key schedule to fend against attacks on the FX structure of PRINCE (see Appendix C), and this improvement has been incorporated in this design.
The improvement involves alternating the keys `k0` and `k1` between rounds, as opposed to always using the same key `k1`.
The reduced 32bit variant mentioned above and all reduced round variants are non-standard and must only be used for scrambling purposes, since they **are not secure**.
The 32bit variant leverages the same crypto primitives and key derivation functions as the 64bit variant, with the difference that the multiplication matrix is only comprised of the first two block diagonal submatrices (^M0 and ^M1 in the paper), and the shiftrows operation does not operate on nibbles but pairs of 2 bits instead.

46
vendor/lowrisc_ip/prim/dv/Makefile vendored Normal file
View file

@ -0,0 +1,46 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
# Entry point test Makefile for building and running tests.
# These are generic set of option groups that apply to all testbenches.
# This flow requires the following options to be set:
# DV_DIR - current dv directory that contains the test Makefile
# DUT_TOP - top level dut module name
# TB_TOP - top level tb module name
# DOTF - .f file used for compilation
# COMPILE_KEY - compile option set
# TEST_NAME - name of the test to run - this is supplied on the command line
DV_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
COMPILE_KEY ?= default
##########################################################
# A D D I N D I V I D U A L T E S T S B E L O W #
##########################################################
ifeq (${TEST_NAME},prim_lfsr_gal_xor)
export DUT_TOP := prim_lfsr
export TB_TOP := prim_lfsr_tb
FUSESOC_CORE := lowrisc:dv:prim_lfsr_sim:0.1
COMPILE_KEY := gal_xor
endif
ifeq (${TEST_NAME},prim_lfsr_fib_xnor)
export DUT_TOP := prim_lfsr
export TB_TOP := prim_lfsr_tb
FUSESOC_CORE := lowrisc:dv:prim_lfsr_sim:0.1
COMPILE_KEY := fib_xnor
endif
ifeq (${COMPILE_KEY},gal_xor)
BUILD_OPTS := +define+LFSR_TYPE="\"GAL_XOR\""+MAX_LFSR_DW=28+MIN_LFSR_DW=4
endif
ifeq (${COMPILE_KEY},fib_xnor)
BUILD_OPTS := +define+LFSR_TYPE="\"FIB_XNOR\""+MAX_LFSR_DW=28+MIN_LFSR_DW=3
endif
####################################
# Include the tool Makefile below #
# Dont add anything else below it! #
####################################
include ${DV_DIR}/../../../dv/tools/Makefile

View file

@ -0,0 +1,28 @@
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:dv:prim_lfsr_sim:0.1"
description: "LFSR DV sim target"
filesets:
files_rtl:
depend:
- lowrisc:prim:all
file_type: systemVerilogSource
files_dv:
depend:
- lowrisc:dv:dv_utils
- lowrisc:dv:common_ifs
files:
- tb/prim_lfsr_tb.sv
- tb/prim_lfsr_bind.sv
file_type: systemVerilogSource
targets:
sim:
toplevel: prim_lfsr_tb
filesets:
- files_rtl
- files_dv
default_tool: vcs

View file

@ -0,0 +1,9 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
module prim_lfsr_bind;
// nothing to bind here yet
endmodule : prim_lfsr_bind

View file

@ -0,0 +1,149 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_lfsr, sweeps through all implementations
// within a certain range to check whether they are max length.
module prim_lfsr_tb;
//////////////////////////////////////////////////////
// config
//////////////////////////////////////////////////////
// this can be overriden on the command line
// supported types are GAL_XOR, FIB_XNOR
`ifdef LFSR_TYPE
localparam string LfsrType = `LFSR_TYPE;
`else
localparam string LfsrType = "GAL_XOR";
`endif
`ifdef MIN_LFSR_DW
localparam int unsigned MinLfsrDw = `MIN_LFSR_DW;
`else
localparam int unsigned MinLfsrDw = 4;
`endif
`ifdef MAX_LFSR_DW
localparam int unsigned MaxLfsrDw = `MAX_LFSR_DW;
`else
localparam int unsigned MaxLfsrDw = 32;
`endif
// leave this constant
localparam logic SEED = 1'b1;
localparam time ClkPeriod = 10000;
//////////////////////////////////////////////////////
// clock
//////////////////////////////////////////////////////
wire clk, rst_n;
clk_rst_if main_clk (
.clk,
.rst_n
);
//////////////////////////////////////////////////////
// DUTs
//////////////////////////////////////////////////////
logic [MaxLfsrDw:0] lfsr_en, err;
logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] state_out;
logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] lfsr_periods;
for (genvar k = MinLfsrDw; k <= MaxLfsrDw; k++) begin : gen_duts
prim_lfsr #(
.LfsrType ( LfsrType ),
.LfsrDw ( k ),
.EntropyDw ( 1 ),
.StateOutDw ( k ),
.DefaultSeed ( k'(SEED) ),
// enable internal max length check
.MaxLenSVA ( 1'b1 )
) i_prim_lfsr (
.clk_i ( clk ),
.rst_ni ( rst_n ),
.seed_en_i ( 1'b0 ),
.seed_i ( '0 ),
.lfsr_en_i ( lfsr_en[k] ),
.entropy_i ( 1'b0 ),
.state_o ( state_out[k][k-1:0] )
);
if (k < MaxLfsrDw) begin : gen_tie_off
assign state_out[k][MaxLfsrDw-1:k] = '0;
end
// calculate period of LFSR:
assign lfsr_periods[k] = MaxLfsrDw'({{(k-1){1'b1}}, 1'b0});
end
//////////////////////////////////////////////////////
// stimuli application / response checking
//////////////////////////////////////////////////////
initial begin : p_stimuli
lfsr_en = '0;
err = '0;
main_clk.set_period_ns(ClkPeriod);
main_clk.set_active();
main_clk.apply_reset();
$display("LFSR maxlen test started for %s (%0d bit to %0d bit).",
LfsrType, MinLfsrDw, MaxLfsrDw);
main_clk.wait_clks(10);
// enable all LFSRs
lfsr_en = '1;
$display("Running for 2**%0d-1 cycles...", MaxLfsrDw);
for (longint unsigned k = 0; k <= lfsr_periods[MaxLfsrDw]; k++ ) begin
main_clk.wait_clks(1);
for (int unsigned j = MinLfsrDw; j <= MaxLfsrDw; j++) begin
// check if we reached the initial state again
if (state_out[j] == MaxLfsrDw'(SEED) && lfsr_en[j]) begin
// $display("cycle: %d -- lfsr: %d -- %x ?= %x, %x",
// k, j, state_out[j], SEED, lfsr_en);
lfsr_en[j] = 1'b0;
// we expect this to occur only after the maximum length period
if (lfsr_periods[j] == k) begin
$display("Maxlen check for LFSR %0d succeeded!", j);
end else begin
err[j] = 1'b1;
$error("Error LFSR %0d is not maximal length!", j);
end
end
end
end
main_clk.wait_clks(10);
for (int unsigned j = MinLfsrDw; j <= MaxLfsrDw; j++) begin
if (lfsr_en[j]) begin
$error("Error LFSR %0d never got back to initial state!", j);
err[j] = 1'b1;
end
end
if (!err) begin
$display("All LFSRs from %0d bit to %0d have maximum length!",
MinLfsrDw, MaxLfsrDw);
// signature for makefile
$display("TEST PASSED CHECKS");
end else begin
$display("One or more checks have failed!");
// signature for makefile
$display("TEST FAILED CHECKS");
end
$finish();
end
endmodule : prim_lfsr_tb

View file

@ -0,0 +1,28 @@
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:fpv:prim_alert_rxtx_async_fpv:0.1"
description: "ALERT_HANDLER rxtx async FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
files:
- vip/prim_alert_rxtx_async_assert_fpv.sv
- tb/prim_alert_rxtx_async_fpv.sv
- tb/prim_alert_rxtx_async_bind_fpv.sv
file_type: systemVerilogSource
targets:
default: &default_target
# note, this setting is just used
# to generate a file list for jg
default_tool: icarus
filesets:
- files_formal
toplevel:
- prim_alert_rxtx_async_fpv
formal:
<<: *default_target

View file

@ -0,0 +1,28 @@
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:fpv:prim_alert_rxtx_fpv:0.1"
description: "ALERT_HANDLER FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
files:
- vip/prim_alert_rxtx_assert_fpv.sv
- tb/prim_alert_rxtx_fpv.sv
- tb/prim_alert_rxtx_bind_fpv.sv
file_type: systemVerilogSource
targets:
default: &default_target
# note, this setting is just used
# to generate a file list for jg
default_tool: icarus
filesets:
- files_formal
toplevel:
- prim_alert_rxtx_fpv
formal:
<<: *default_target

View file

@ -0,0 +1,28 @@
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:fpv:prim_arbiter_ppc_fpv:0.1"
description: "prim_arbiter_ppc FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
# TODO: add more dependencies here if needed
files:
- vip/prim_arbiter_ppc_assert_fpv.sv
- tb/prim_arbiter_ppc_bind_fpv.sv
- tb/prim_arbiter_ppc_fpv.sv
file_type: systemVerilogSource
targets:
default: &default_target
# note, this setting is just used
# to generate a file list for jg
default_tool: icarus
filesets:
- files_formal
toplevel: prim_arbiter_ppc_fpv
formal:
<<: *default_target

View file

@ -0,0 +1,28 @@
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:fpv:prim_arbiter_tree_fpv:0.1"
description: "prim_arbiter_tree FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
# TODO: add more dependencies here if needed
files:
- vip/prim_arbiter_tree_assert_fpv.sv
- tb/prim_arbiter_tree_bind_fpv.sv
- tb/prim_arbiter_tree_fpv.sv
file_type: systemVerilogSource
targets:
default: &default_target
# note, this setting is just used
# to generate a file list for jg
default_tool: icarus
filesets:
- files_formal
toplevel: prim_arbiter_tree_fpv
formal:
<<: *default_target

View file

@ -0,0 +1,28 @@
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:fpv:prim_esc_rxtx_fpv:0.1"
description: "ALERT_HANDLER FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
files:
- vip/prim_esc_rxtx_assert_fpv.sv
- tb/prim_esc_rxtx_bind_fpv.sv
- tb/prim_esc_rxtx_fpv.sv
file_type: systemVerilogSource
targets:
default: &default_target
# note, this setting is just used
# to generate a file list for jg
formal: icarus
filesets:
- files_formal
toplevel:
- prim_esc_rxtx_fpv
formal:
<<: *default_target

View file

@ -0,0 +1,27 @@
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:fpv:prim_fifo_sync_fpv:0.1"
description: "prim_fifo_sync FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
files:
- vip/prim_fifo_sync_assert_fpv.sv
- tb/prim_fifo_sync_bind_fpv.sv
- tb/prim_fifo_sync_fpv.sv
file_type: systemVerilogSource
targets:
default: &default_target
# note, this setting is just used
# to generate a file list for jg
default_tool: icarus
filesets:
- files_formal
toplevel: prim_fifo_sync_fpv
formal:
<<: *default_target

View file

@ -0,0 +1,23 @@
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:fpv:prim_keccak_fpv:0.1"
description: "Keccak_f FPV target"
filesets:
files_fpv:
depend:
- lowrisc:prim:all
files:
- tb/prim_keccak_fpv.sv
file_type: systemVerilogSource
targets:
default:
# note, this setting is just used
# to generate a file list for jg
default_tool: icarus
filesets:
- files_fpv
toplevel:
- prim_keccak_fpv

View file

@ -0,0 +1,26 @@
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:fpv:prim_lfsr_fpv:0.1"
description: "ALERT_HANDLER FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
files:
- tb/prim_lfsr_fpv.sv
file_type: systemVerilogSource
targets:
default: &default_target
# note, this setting is just used
# to generate a file list for jg
default_tool: icarus
filesets:
- files_formal
toplevel:
- prim_lfsr_fpv
formal:
<<: *default_target

View file

@ -0,0 +1,28 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
module prim_alert_rxtx_async_bind_fpv;
bind prim_alert_rxtx_async_fpv
prim_alert_rxtx_async_assert_fpv prim_alert_rxtx_async_assert_fpv (
.clk_i,
.rst_ni,
.ping_err_pi,
.ping_err_ni,
.ping_skew_i,
.ack_err_pi,
.ack_err_ni,
.ack_skew_i,
.alert_err_pi,
.alert_err_ni,
.alert_skew_i,
.alert_i,
.ping_en_i,
.ping_ok_o,
.integ_fail_o,
.alert_o
);
endmodule : prim_alert_rxtx_async_bind_fpv

View file

@ -0,0 +1,109 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for alert sender/receiver pair. Intended to use with
// a formal tool.
module prim_alert_rxtx_async_fpv
import prim_alert_pkg::*;
import prim_esc_pkg::*;
(
input clk_i,
input rst_ni,
// for sigint error and skew injection only
input ping_err_pi,
input ping_err_ni,
input [1:0] ping_skew_i,
input ack_err_pi,
input ack_err_ni,
input [1:0] ack_skew_i,
input alert_err_pi,
input alert_err_ni,
input [1:0] alert_skew_i,
// normal I/Os
input alert_i,
input ping_en_i,
output logic ping_ok_o,
output logic integ_fail_o,
output logic alert_o
);
// asynchronous case
localparam bit AsyncOn = 1'b1;
logic ping_pd;
logic ping_nd;
logic ack_pd;
logic ack_nd;
logic alert_pd;
logic alert_nd;
alert_rx_t alert_rx_out, alert_rx_in;
alert_tx_t alert_tx_out, alert_tx_in;
// for the purposes of FPV, we currently emulate the asynchronous transition
// only in terms of the skew it may introduce (which is limited to +- 1 cycle)
logic [1:0] ping_pq;
logic [1:0] ping_nq;
logic [1:0] ack_pq;
logic [1:0] ack_nq;
logic [1:0] alert_pq;
logic [1:0] alert_nq;
assign ping_pd = alert_rx_out.ping_p;
assign ping_nd = alert_rx_out.ping_n;
assign ack_pd = alert_rx_out.ack_p;
assign ack_nd = alert_rx_out.ack_n;
assign alert_rx_in.ping_p = ping_pq[ping_skew_i[0]] ^ ping_err_pi;
assign alert_rx_in.ping_n = ping_nq[ping_skew_i[1]] ^ ping_err_ni;
assign alert_rx_in.ack_p = ack_pq[ack_skew_i[0]] ^ ack_err_pi;
assign alert_rx_in.ack_n = ack_nq[ack_skew_i[1]] ^ ack_err_ni;
assign alert_pd = alert_tx_out.alert_p;
assign alert_nd = alert_tx_out.alert_n;
assign alert_tx_in.alert_p = alert_pq[alert_skew_i[0]] ^ alert_err_pi;
assign alert_tx_in.alert_n = alert_nq[alert_skew_i[1]] ^ alert_err_ni;
prim_alert_sender #(
.AsyncOn ( AsyncOn )
) i_prim_alert_sender (
.clk_i ,
.rst_ni ,
.alert_i ,
.alert_rx_i ( alert_rx_in ),
.alert_tx_o ( alert_tx_out )
);
prim_alert_receiver #(
.AsyncOn ( AsyncOn )
) i_prim_alert_receiver (
.clk_i ,
.rst_ni ,
.ping_en_i ,
.ping_ok_o ,
.integ_fail_o ,
.alert_o ,
.alert_rx_o ( alert_rx_out ),
.alert_tx_i ( alert_tx_in )
);
always_ff @(posedge clk_i or negedge rst_ni) begin : p_skew_delay
if (!rst_ni) begin
ping_pq <= '0;
ping_nq <= '1;
ack_pq <= '0;
ack_nq <= '1;
alert_pq <= '0;
alert_nq <= '1;
end else begin
ping_pq <= {ping_pq [$high(ping_pq )-1:0], ping_pd};
ping_nq <= {ping_nq [$high(ping_nq )-1:0], ping_nd};
ack_pq <= {ack_pq [$high(ack_pq )-1:0], ack_pd};
ack_nq <= {ack_nq [$high(ack_nq )-1:0], ack_nd};
alert_pq <= {alert_pq[$high(alert_pq)-1:0], alert_pd};
alert_nq <= {alert_nq[$high(alert_nq)-1:0], alert_nd};
end
end
endmodule : prim_alert_rxtx_async_fpv

View file

@ -0,0 +1,25 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
module prim_alert_rxtx_bind_fpv;
bind prim_alert_rxtx_fpv
prim_alert_rxtx_assert_fpv prim_alert_rxtx_assert_fpv (
.clk_i,
.rst_ni,
.ping_err_pi,
.ping_err_ni,
.ack_err_pi,
.ack_err_ni,
.alert_err_pi,
.alert_err_ni,
.alert_i,
.ping_en_i,
.ping_ok_o,
.integ_fail_o,
.alert_o
);
endmodule : prim_alert_rxtx_bind_fpv

View file

@ -0,0 +1,66 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for alert sender/receiver pair. Intended to use with
// a formal tool.
module prim_alert_rxtx_fpv
import prim_alert_pkg::*;
import prim_esc_pkg::*;
(
input clk_i,
input rst_ni,
// for sigint error injection only
input ping_err_pi,
input ping_err_ni,
input ack_err_pi,
input ack_err_ni,
input alert_err_pi,
input alert_err_ni,
// normal I/Os
input alert_i,
input ping_en_i,
output logic ping_ok_o,
output logic integ_fail_o,
output logic alert_o
);
// synchronous case
localparam bit AsyncOn = 1'b0;
alert_rx_t alert_rx_out, alert_rx_in;
alert_tx_t alert_tx_out, alert_tx_in;
assign alert_rx_in.ping_p = alert_rx_out.ping_p ^ ping_err_pi;
assign alert_rx_in.ping_n = alert_rx_out.ping_n ^ ping_err_ni;
assign alert_rx_in.ack_p = alert_rx_out.ack_p ^ ack_err_pi;
assign alert_rx_in.ack_n = alert_rx_out.ack_n ^ ack_err_ni;
assign alert_tx_in.alert_p = alert_tx_out.alert_p ^ alert_err_pi;
assign alert_tx_in.alert_n = alert_tx_out.alert_n ^ alert_err_ni;
prim_alert_sender #(
.AsyncOn ( AsyncOn )
) i_prim_alert_sender (
.clk_i ,
.rst_ni ,
.alert_i ,
.alert_rx_i ( alert_rx_in ),
.alert_tx_o ( alert_tx_out )
);
prim_alert_receiver #(
.AsyncOn ( AsyncOn )
) i_prim_alert_receiver (
.clk_i ,
.rst_ni ,
.ping_en_i ,
.ping_ok_o ,
.integ_fail_o ,
.alert_o ,
.alert_rx_o ( alert_rx_out ),
.alert_tx_i ( alert_tx_in )
);
endmodule : prim_alert_rxtx_fpv

View file

@ -0,0 +1,25 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
module prim_arbiter_ppc_bind_fpv;
bind prim_arbiter_ppc prim_arbiter_ppc_assert_fpv #(
.N(N),
.DW(DW)
) i_prim_arbiter_ppc_assert_fpv (
.clk_i,
.rst_ni,
.req_i,
.data_i,
.gnt_o,
.idx_o,
.valid_o,
.data_o,
.ready_i
);
endmodule : prim_arbiter_ppc_bind_fpv

View file

@ -0,0 +1,40 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_arbiter_ppc.
// Intended to be used with a formal tool.
module prim_arbiter_ppc_fpv #(
parameter int unsigned N = 4,
parameter int unsigned DW = 32
) (
input clk_i,
input rst_ni,
input [N-1:0] req_i,
input [DW-1:0]data_i [N],
output logic[N-1:0] gnt_o,
output logic[$clog2(N)-1:0] idx_o,
output logic valid_o,
output logic[DW-1:0] data_o,
input ready_i
);
prim_arbiter_ppc #(
.N(N),
.DW(DW)
) i_prim_arbiter_ppc (
.clk_i,
.rst_ni,
.req_i,
.data_i,
.gnt_o,
.idx_o,
.valid_o,
.data_o,
.ready_i
);
endmodule : prim_arbiter_ppc_fpv

View file

@ -0,0 +1,26 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
module prim_arbiter_tree_bind_fpv;
bind prim_arbiter_tree prim_arbiter_tree_assert_fpv #(
.N(N),
.DW(DW),
.Lock(Lock)
) i_prim_arbiter_tree_assert_fpv (
.clk_i,
.rst_ni,
.req_i,
.data_i,
.gnt_o,
.idx_o,
.valid_o,
.data_o,
.ready_i
);
endmodule : prim_arbiter_tree_bind_fpv

View file

@ -0,0 +1,40 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_arbiter_tree.
// Intended to be used with a formal tool.
module prim_arbiter_tree_fpv #(
parameter int unsigned N = 4,
parameter int unsigned DW = 32,
parameter bit Lock = 1'b1
) (
input clk_i,
input rst_ni,
input [N-1:0] req_i,
input [DW-1:0]data_i [N],
output logic[N-1:0] gnt_o,
output logic[$clog2(N)-1:0] idx_o,
output logic valid_o,
output logic[DW-1:0] data_o,
input ready_i
);
prim_arbiter_tree #(
.N(N),
.DW(DW),
.Lock(Lock)
) i_prim_arbiter_tree (
.clk_i,
.rst_ni,
.req_i,
.data_i,
.gnt_o,
.idx_o,
.valid_o,
.data_o,
.ready_i
);
endmodule : prim_arbiter_tree_fpv

View file

@ -0,0 +1,23 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
module prim_esc_rxtx_bind_fpv;
bind prim_esc_rxtx_fpv
prim_esc_rxtx_assert_fpv prim_esc_rxtx_assert_fpv (
.clk_i ,
.rst_ni ,
.resp_err_pi ,
.resp_err_ni ,
.esc_err_pi ,
.esc_err_ni ,
.esc_en_i ,
.ping_en_i ,
.ping_ok_o ,
.integ_fail_o,
.esc_en_o
);
endmodule : prim_esc_rxtx_bind_fpv

View file

@ -0,0 +1,54 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for escalation sender/receiver pair. Intended to use with
// a formal tool.
module prim_esc_rxtx_fpv
import prim_alert_pkg::*;
import prim_esc_pkg::*;
(
input clk_i,
input rst_ni,
// for sigint error injection only
input resp_err_pi,
input resp_err_ni,
input esc_err_pi,
input esc_err_ni,
// normal I/Os
input esc_en_i,
input ping_en_i,
output logic ping_ok_o,
output logic integ_fail_o,
output logic esc_en_o
);
esc_rx_t esc_rx_in, esc_rx_out;
esc_tx_t esc_tx_in, esc_tx_out;
assign esc_rx_in.resp_p = esc_rx_out.resp_p ^ resp_err_pi;
assign esc_rx_in.resp_n = esc_rx_out.resp_n ^ resp_err_ni;
assign esc_tx_in.esc_p = esc_tx_out.esc_p ^ esc_err_pi;
assign esc_tx_in.esc_n = esc_tx_out.esc_n ^ esc_err_ni;
prim_esc_sender i_prim_esc_sender (
.clk_i ,
.rst_ni ,
.ping_en_i ,
.ping_ok_o ,
.integ_fail_o ,
.esc_en_i ,
.esc_rx_i ( esc_rx_in ),
.esc_tx_o ( esc_tx_out )
);
prim_esc_receiver i_prim_esc_receiver (
.clk_i ,
.rst_ni ,
.esc_en_o ,
.esc_rx_o ( esc_rx_out ),
.esc_tx_i ( esc_tx_in )
);
endmodule : prim_esc_rxtx_fpv

View file

@ -0,0 +1,219 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
module prim_fifo_sync_bind_fpv;
localparam int unsigned Width = 4;
// need to instantiate by hand since bind statements inside
// generate blocks are currently not supported
////////////////////
// non-pass FIFOs //
////////////////////
bind i_nopass1 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b0),
.Depth(1),
.EnableDataCheck(1'b1)
) i_nopass1_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
bind i_nopass7 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b0),
.Depth(7),
.EnableDataCheck(1'b1)
) i_nopass7_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
bind i_nopass8 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b0),
.Depth(8),
.EnableDataCheck(1'b1)
) i_nopass8_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
bind i_nopass15 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b0),
.Depth(15),
.EnableDataCheck(1'b0)
) i_nopass15_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
bind i_nopass16 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b0),
.Depth(16),
.EnableDataCheck(1'b0)
) i_nopass16_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
////////////////
// pass FIFOs //
////////////////
bind i_pass0 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b1),
.Depth(0),
.EnableDataCheck(1'b1)
) i_pass0_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
bind i_pass1 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b1),
.Depth(1),
.EnableDataCheck(1'b1)
) i_pass1_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
bind i_pass7 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b1),
.Depth(7),
.EnableDataCheck(1'b1)
) i_pass7_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
bind i_pass8 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b1),
.Depth(8),
.EnableDataCheck(1'b1)
) i_pass8_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
bind i_pass15 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b1),
.Depth(15),
.EnableDataCheck(1'b0)
) i_pass15_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
bind i_pass16 prim_fifo_sync_assert_fpv #(
.Width(Width),
.Pass(1'b1),
.Depth(16),
.EnableDataCheck(1'b0)
) i_pass16_assert_fpv (
.clk_i,
.rst_ni,
.clr_i,
.wvalid,
.wready,
.wdata,
.rvalid,
.rready,
.rdata,
.depth
);
endmodule : prim_fifo_sync_bind_fpv

View file

@ -0,0 +1,236 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_fifo_sync.
// Intended to be used with a formal tool.
//
// This formal testbench instantiates a set of differently parameterized FIFOs:
//
// - a depth 0 pass through FIFO
// - depth 1, 7, 8, 15, 16 pass through FIFOs
// - depth 1, 7, 8, 15, 16 non-pass through FIFOs
//
// Data/depth value checks are enabled up to depth 8 in order to constrain the
// runtime.
module prim_fifo_sync_fpv #(
// number of DUTs instantiated in this FPV testbench
parameter int unsigned NumDuts = 11,
// fifo params
parameter int unsigned Width = 4,
parameter int unsigned MaxDepth = 16, // max depth used in this destbench
localparam int unsigned DepthW = ($clog2(MaxDepth+1) == 0) ? 1 : $clog2(MaxDepth+1)
) (
input clk_i,
input rst_ni,
input clr_i,
input wvalid [NumDuts],
output wready [NumDuts],
input [Width-1:0] wdata [NumDuts],
output rvalid [NumDuts],
input rready [NumDuts],
output [Width-1:0] rdata [NumDuts],
output [DepthW-1:0] depth [NumDuts]
);
// need to instantiate by hand since bind statements inside
// generate blocks are currently not supported
////////////////////
// non-pass FIFOs //
////////////////////
prim_fifo_sync #(
.Width(Width),
.Pass(1'b0),
.Depth(1)
) i_nopass1 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[0]),
.wready(wready[0]),
.wdata(wdata[0]),
.rvalid(rvalid[0]),
.rready(rready[0]),
.rdata(rdata[0]),
.depth(depth[0][0])
);
prim_fifo_sync #(
.Width(Width),
.Pass(1'b0),
.Depth(7)
) i_nopass7 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[1]),
.wready(wready[1]),
.wdata(wdata[1]),
.rvalid(rvalid[1]),
.rready(rready[1]),
.rdata(rdata[1]),
.depth(depth[1][2:0])
);
prim_fifo_sync #(
.Width(Width),
.Pass(1'b0),
.Depth(8)
) i_nopass8 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[2]),
.wready(wready[2]),
.wdata(wdata[2]),
.rvalid(rvalid[2]),
.rready(rready[2]),
.rdata(rdata[2]),
.depth(depth[2][3:0])
);
prim_fifo_sync #(
.Width(Width),
.Pass(1'b0),
.Depth(15)
) i_nopass15 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[3]),
.wready(wready[3]),
.wdata(wdata[3]),
.rvalid(rvalid[3]),
.rready(rready[3]),
.rdata(rdata[3]),
.depth(depth[3][3:0])
);
prim_fifo_sync #(
.Width(Width),
.Pass(1'b0),
.Depth(16)
) i_nopass16 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[4]),
.wready(wready[4]),
.wdata(wdata[4]),
.rvalid(rvalid[4]),
.rready(rready[4]),
.rdata(rdata[4]),
.depth(depth[4][4:0])
);
////////////////
// pass FIFOs //
////////////////
// depth-zero is per definition a pass-through FIFO
prim_fifo_sync #(
.Width(Width),
.Pass(1'b1),
.Depth(0)
) i_pass0 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[5]),
.wready(wready[5]),
.wdata(wdata[5]),
.rvalid(rvalid[5]),
.rready(rready[5]),
.rdata(rdata[5]),
.depth(depth[5][0])
);
prim_fifo_sync #(
.Width(Width),
.Pass(1'b1),
.Depth(1)
) i_pass1 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[6]),
.wready(wready[6]),
.wdata(wdata[6]),
.rvalid(rvalid[6]),
.rready(rready[6]),
.rdata(rdata[6]),
.depth(depth[6][0])
);
prim_fifo_sync #(
.Width(Width),
.Pass(1'b1),
.Depth(7)
) i_pass7 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[7]),
.wready(wready[7]),
.wdata(wdata[7]),
.rvalid(rvalid[7]),
.rready(rready[7]),
.rdata(rdata[7]),
.depth(depth[7][2:0])
);
prim_fifo_sync #(
.Width(Width),
.Pass(1'b1),
.Depth(8)
) i_pass8 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[8]),
.wready(wready[8]),
.wdata(wdata[8]),
.rvalid(rvalid[8]),
.rready(rready[8]),
.rdata(rdata[8]),
.depth(depth[8][3:0])
);
prim_fifo_sync #(
.Width(Width),
.Pass(1'b1),
.Depth(15)
) i_pass15 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[9]),
.wready(wready[9]),
.wdata(wdata[9]),
.rvalid(rvalid[9]),
.rready(rready[9]),
.rdata(rdata[9]),
.depth(depth[9][3:0])
);
prim_fifo_sync #(
.Width(Width),
.Pass(1'b1),
.Depth(16)
) i_pass16 (
.clk_i,
.rst_ni,
.clr_i,
.wvalid(wvalid[10]),
.wready(wready[10]),
.wdata(wdata[10]),
.rvalid(rvalid[10]),
.rready(rready[10]),
.rdata(rdata[10]),
.depth(depth[10][4:0])
);
endmodule : prim_fifo_sync_fpv

View file

@ -0,0 +1,74 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_keccak. Intended to be used with a formal tool.
module prim_keccak_fpv #(
parameter int Width = 1600
) (
input clk_i,
input rst_ni,
input valid_i,
input [Width-1:0] state_i,
output logic done_o,
output logic [Width-1:0] state_o
);
localparam int W = Width/25;
localparam int L = $clog2(W);
localparam int NumRound = 12 + 2*L; // Keccak-f only
localparam int RndW = $clog2(NumRound+1);
logic [RndW-1:0] round;
logic active;
logic [Width-1:0] state, state_d;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) state <= '0;
else if (valid_i) state <= state_i;
else if (active) state <= state_d;
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) round <= '0;
else if (valid_i) round <= '0;
else if (active) round <= round + 1'b 1;
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) active <= 1'b 0;
else if (valid_i) active <= 1'b 1;
else if (round == (NumRound -1)) active <= 1'b 0;
end
assign done_o = (round == NumRound);
assign state_o = state;
prim_keccak #(
.Width (Width)
) u_keccak (
.rnd_i (round),
.s_i (state),
.s_o (state_d)
);
`ASSUME_FPV(ValidSequence_A, ##1 valid_i |=> !valid_i)
`ASSUME_FPV(ValidValid_A, active |-> !valid_i)
// Test with value 0
logic [1599:0] data_0 ;
always_comb begin
data_0 = '0;
// SHA3-256 ==> r : 1088
data_0[1087] = 1'b 1;
data_0[2:0] = 3'b 110;
end
logic [255:0] digest_0;
// Big-Endian a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a
assign digest_0 = 256'h 4a43_f880_4b0a_d882_fa49_3be4_4dff_80f5_62d6_61a0_5647_c151_66d7_1ebf_f8c6_ffa7;
`ASSUME_FPV(Data0TestSHA3_256_A, state_i == data_0)
`ASSERT(DigestForData0TestSHA3_256_A, done_o |-> state_o[255:0] == digest_0)
endmodule

View file

@ -0,0 +1,75 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_lfsr. Intended to be used with a formal tool.
module prim_lfsr_fpv #(
// LFSR entropy and output bitwidths (set to 1 here as they are unused)
parameter int unsigned EntropyDw = 1,
parameter int unsigned StateOutDw = 1,
// this specifies the range of differently
// parameterized LFSRs to instantiate and check
parameter int unsigned GalXorMinLfsrDw = 4,
parameter int unsigned GalXorMaxLfsrDw = 64,
parameter int unsigned FibXnorMinLfsrDw = 3,
parameter int unsigned FibXnorMaxLfsrDw = 168,
// LFSRs up to this bitwidth are checked for maximum length
parameter int unsigned MaxLenSVAThresh = 10,
// derived params
localparam int unsigned GalMaxGtFibMax = GalXorMaxLfsrDw > FibXnorMaxLfsrDw,
localparam int unsigned MaxLfsrDw = GalXorMaxLfsrDw * GalMaxGtFibMax +
FibXnorMaxLfsrDw * (1 - GalMaxGtFibMax),
localparam int unsigned NumDuts = FibXnorMaxLfsrDw - FibXnorMinLfsrDw + 1 +
GalXorMaxLfsrDw - GalXorMinLfsrDw + 1
) (
input clk_i,
input rst_ni,
input [NumDuts-1:0] load_ext_en_i,
input [NumDuts-1:0][MaxLfsrDw-1:0] seed_ext_i,
input [NumDuts-1:0] lfsr_en_i,
input [NumDuts-1:0][EntropyDw-1:0] entropy_i,
output logic [NumDuts-1:0][StateOutDw-1:0] state_o
);
for (genvar k = GalXorMinLfsrDw; k <= GalXorMaxLfsrDw; k++) begin : gen_gal_xor_duts
localparam int unsigned idx = k - GalXorMinLfsrDw;
prim_lfsr #(.LfsrType("GAL_XOR"),
.LfsrDw ( k ),
.EntropyDw ( EntropyDw ),
.StateOutDw ( StateOutDw ),
.DefaultSeed ( 1 ),
// disabled for large LFSRs since this becomes prohibitive in runtime
.MaxLenSVA ( k <= MaxLenSVAThresh )
) i_prim_lfsr (
.clk_i,
.rst_ni,
.seed_en_i ( load_ext_en_i[idx] ),
.seed_i ( k'(seed_ext_i[idx]) ),
.lfsr_en_i ( lfsr_en_i[idx] ),
.entropy_i ( entropy_i[idx] ),
.state_o ( state_o[idx] )
);
end
for (genvar k = FibXnorMinLfsrDw; k <= FibXnorMaxLfsrDw; k++) begin : gen_fib_xnor_duts
localparam int unsigned idx = k - FibXnorMinLfsrDw + GalXorMaxLfsrDw - GalXorMinLfsrDw + 1;
prim_lfsr #(.LfsrType("FIB_XNOR"),
.LfsrDw ( k ),
.EntropyDw ( EntropyDw ),
.StateOutDw ( StateOutDw ),
.DefaultSeed ( 1 ),
// disabled for large LFSRs since this becomes prohibitive in runtime
.MaxLenSVA ( k <= MaxLenSVAThresh )
) i_prim_lfsr (
.clk_i,
.rst_ni,
.seed_en_i ( load_ext_en_i[idx] ),
.seed_i ( k'(seed_ext_i[idx]) ),
.lfsr_en_i ( lfsr_en_i[idx] ),
.entropy_i ( entropy_i[idx] ),
.state_o ( state_o[idx] )
);
end
endmodule : prim_lfsr_fpv

View file

@ -0,0 +1,92 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Assertions for alert sender/receiver pair. Intended to use with
// a formal tool.
`include "prim_assert.sv"
module prim_alert_rxtx_assert_fpv (
input clk_i,
input rst_ni,
// for sigint error injection only
input ping_err_pi,
input ping_err_ni,
input ack_err_pi,
input ack_err_ni,
input alert_err_pi,
input alert_err_ni,
// normal I/Os
input alert_i,
input ping_en_i,
input ping_ok_o,
input integ_fail_o,
input alert_o
);
logic error_present;
assign error_present = ping_err_pi | ping_err_ni |
ack_err_pi | ack_err_ni |
alert_err_pi | alert_err_ni;
// note: we can only detect sigint errors where one wire is flipped.
`ASSUME_FPV(PingErrorsAreOH_M, $onehot0({ping_err_pi, ping_err_ni}), clk_i, !rst_ni)
`ASSUME_FPV(AckErrorsAreOH_M, $onehot0({ack_err_pi, ack_err_ni}), clk_i, !rst_ni)
`ASSUME_FPV(AlertErrorsAreOH_M, $onehot0({alert_err_pi, alert_err_ni}), clk_i, !rst_ni)
// ping will stay high until ping ok received, then it must be deasserted
// TODO: this excludes the case where no ping ok will be returned due to an error
`ASSUME_FPV(PingDeassert_M, ping_en_i && ping_ok_o |=> !ping_en_i, clk_i, !rst_ni)
`ASSUME_FPV(PingEnStaysAsserted0_M, ping_en_i |=> (ping_en_i && !ping_ok_o) or
(ping_en_i && ping_ok_o ##1 $fell(ping_en_i)), clk_i, !rst_ni || error_present)
sequence FullHandshake_S;
$rose(prim_alert_rxtx_fpv.alert_tx_out.alert_p) ##1
$rose(prim_alert_rxtx_fpv.alert_rx_out.ack_p) &&
$stable(prim_alert_rxtx_fpv.alert_tx_out.alert_p) ##1
$fell(prim_alert_rxtx_fpv.alert_tx_out.alert_p) &&
$stable(prim_alert_rxtx_fpv.alert_rx_out.ack_p) ##1
$fell(prim_alert_rxtx_fpv.alert_rx_out.ack_p) &&
$stable(prim_alert_rxtx_fpv.alert_tx_out.alert_p) ;
endsequence
// note: injected errors may lockup the FSMs, and hence the full HS can
// only take place if both FSMs are in a sane state
`ASSERT(PingHs_A, ##1 $changed(prim_alert_rxtx_fpv.alert_rx_out.ping_p) &&
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle ) &&
(prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle )|=> FullHandshake_S,
clk_i, !rst_ni || error_present)
`ASSERT(AlertHs_A, alert_i &&
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle) |=>
FullHandshake_S, clk_i, !rst_ni || error_present)
// transmission of pings
`ASSERT(AlertPing_A, !error_present ##1 $rose(ping_en_i) |->
##[1:9] ping_ok_o, clk_i, !rst_ni || error_present)
// transmission of alerts in case of no collision with ping enable
`ASSERT(AlertCheck0_A, !ping_en_i [*3] ##0 $rose(alert_i) &&
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) |=>
alert_o, clk_i, !rst_ni || error_present || ping_en_i)
// transmission of alerts in the general case which can include ping collisions
`ASSERT(AlertCheck1_A, alert_i |-> ##[1:9] alert_o, clk_i, !rst_ni || error_present)
// basic liveness of FSMs in case no errors are present
`ASSERT(FsmLivenessSender_A,
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q !=
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) |->
strong(##[1:$] (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle)), clk_i, !rst_ni || error_present)
`ASSERT(FsmLivenessReceiver_A,
(prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q !=
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle) |->
strong(##[1:$] (prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle)),clk_i, !rst_ni || error_present)
endmodule : prim_alert_rxtx_assert_fpv

View file

@ -0,0 +1,121 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Assertions for alert sender/receiver pair for the asynchronous case.
// Intended to use with a formal tool.
`include "prim_assert.sv"
module prim_alert_rxtx_async_assert_fpv (
input clk_i,
input rst_ni,
// for sigint error and skew injection only
input ping_err_pi,
input ping_err_ni,
input [1:0] ping_skew_i,
input ack_err_pi,
input ack_err_ni,
input [1:0] ack_skew_i,
input alert_err_pi,
input alert_err_ni,
input [1:0] alert_skew_i,
// normal I/Os
input alert_i,
input ping_en_i,
input ping_ok_o,
input integ_fail_o,
input alert_o
);
logic error_present;
assign error_present = ping_err_pi | ping_err_ni |
ack_err_pi | ack_err_ni |
alert_err_pi | alert_err_ni;
// used to check that an error has never occured so far
// this is used to check the handshake below. the handshake can lock up
// the protocol FSMs causing the handshake to never complete.
// note that this will block any ping messages and hence it can be
// eventually detected by the alert handler.
logic error_setreg_d, error_setreg_q;
assign error_setreg_d = error_present | error_setreg_q;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
if (!rst_ni) begin
error_setreg_q <= 1'b0;
end else begin
error_setreg_q <= error_setreg_d;
end
end
// Note: we can only detect sigint errors where one wire is flipped.
`ASSUME_FPV(PingErrorsAreOH_M, $onehot0({ping_err_pi, ping_err_ni}) )
`ASSUME_FPV(AckErrorsAreOH_M, $onehot0({ack_err_pi, ack_err_ni}) )
`ASSUME_FPV(AlertErrorsAreOH_M, $onehot0({alert_err_pi, alert_err_ni}))
// ping will stay high until ping ok received, then it must be deasserted
// TODO: this excludes the case where no ping ok will be returned due to an error
`ASSUME_FPV(PingDeassert_M, ping_en_i && ping_ok_o |=> !ping_en_i, clk_i, !rst_ni)
`ASSUME_FPV(PingEnStaysAsserted0_M, ping_en_i |=>
(ping_en_i && !ping_ok_o) or
(ping_en_i && ping_ok_o ##1 $fell(ping_en_i)),
clk_i, !rst_ni || error_present)
// Note: the sequence lengths of the handshake and the following properties needs to
// be parameterized accordingly if different clock ratios are to be used here.
// TODO: tighten bounds if possible
sequence FullHandshake_S;
$rose(prim_alert_rxtx_async_fpv.alert_pd) ##[3:5]
$rose(prim_alert_rxtx_async_fpv.ack_pd) &&
$stable(prim_alert_rxtx_async_fpv.alert_pd) ##[3:5]
$fell(prim_alert_rxtx_async_fpv.alert_pd) &&
$stable(prim_alert_rxtx_async_fpv.ack_pd) ##[3:5]
$fell(prim_alert_rxtx_async_fpv.ack_pd) &&
$stable(prim_alert_rxtx_async_fpv.alert_pd);
endsequence
// note: injected errors may lockup the FSMs, and hence the full HS can
// only take place if both FSMs are in a sane state
`ASSERT(PingHs_A, ##1 $changed(prim_alert_rxtx_async_fpv.ping_pd) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |-> ##[0:5] FullHandshake_S,
clk_i, !rst_ni || error_setreg_q)
`ASSERT(AlertHs_A, alert_i &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |-> ##[0:5] FullHandshake_S,
clk_i, !rst_ni || error_setreg_q)
// transmission of pings
// this bound is relatively large as in the worst case, we need to resolve
// staggered differential signal patterns on all three differential channels
`ASSERT(AlertPing_A, $rose(ping_en_i) |-> ##[1:23] ping_ok_o,
clk_i, !rst_ni || error_setreg_q)
// transmission of first alert assertion (no ping collision)
`ASSERT(AlertCheck0_A, !ping_en_i [*10] ##1 $rose(alert_i) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) |->
##[3:5] alert_o, clk_i, !rst_ni || ping_en_i || error_setreg_q)
// eventual transmission of alerts in the general case which can include ping collisions
`ASSERT(AlertCheck1_A, alert_i |-> ##[1:25] alert_o,
clk_i, !rst_ni || error_setreg_q)
// basic liveness of FSMs in case no errors are present
`ASSERT(FsmLivenessSender_A, !error_present [*2] ##1 !error_present &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q !=
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) |->
strong(##[1:$] (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle)), clk_i, !rst_ni || error_present)
`ASSERT(FsmLivenessReceiver_A, !error_present [*2] ##1 !error_present &&
(prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q !=
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |->
strong(##[1:$] (prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle)),clk_i, !rst_ni || error_present)
// TODO: add FSM liveness of 3x diff decoder instances
endmodule : prim_alert_rxtx_async_assert_fpv

View file

@ -0,0 +1,47 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Assertions for prim_arbiter_ppc.
// Intended to be used with a formal tool.
`include "prim_assert.sv"
module prim_arbiter_ppc_assert_fpv #(
parameter int unsigned N = 4,
parameter int unsigned DW = 32
) (
input clk_i,
input rst_ni,
input [N-1:0] req_i,
input [DW-1:0]data_i [N],
input logic[N-1:0] gnt_o,
input logic[$clog2(N)-1:0] idx_o,
input logic valid_o,
input logic[DW-1:0] data_o,
input ready_i
);
///////////////////////////////
// Declarations & Parameters //
///////////////////////////////
/////////////////
// Assumptions //
/////////////////
// `ASSUME(MyAssumption_M, ..., clk_i, !rst_ni)
////////////////////////
// Forward Assertions //
////////////////////////
// `ASSERT(MyFwdAssertion_A, ..., clk_i, !rst_ni)
/////////////////////////
// Backward Assertions //
/////////////////////////
// `ASSERT(MyBkwdAssertion_A, ..., clk_i, !rst_ni)
endmodule : prim_arbiter_ppc_assert_fpv

View file

@ -0,0 +1,48 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Assertions for prim_arbiter_tree.
// Intended to be used with a formal tool.
`include "prim_assert.sv"
module prim_arbiter_tree_assert_fpv #(
parameter int unsigned N = 4,
parameter int unsigned DW = 32,
parameter bit Lock = 1'b1
) (
input clk_i,
input rst_ni,
input [N-1:0] req_i,
input [DW-1:0]data_i [N],
input logic[N-1:0] gnt_o,
input logic[$clog2(N)-1:0] idx_o,
input logic valid_o,
input logic[DW-1:0] data_o,
input ready_i
);
///////////////////////////////
// Declarations & Parameters //
///////////////////////////////
/////////////////
// Assumptions //
/////////////////
// `ASSUME(MyAssumption_M, ..., clk_i, !rst_ni)
////////////////////////
// Forward Assertions //
////////////////////////
// `ASSERT(MyFwdAssertion_A, ..., clk_i, !rst_ni)
/////////////////////////
// Backward Assertions //
/////////////////////////
// `ASSERT(MyBkwdAssertion_A, ..., clk_i, !rst_ni)
endmodule : prim_arbiter_tree_assert_fpv

View file

@ -0,0 +1,70 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Assertions for escalation sender/receiver pair. Intended to use with
// a formal tool.
`include "prim_assert.sv"
module prim_esc_rxtx_assert_fpv (
input clk_i,
input rst_ni,
// for sigint error injection only
input resp_err_pi,
input resp_err_ni,
input esc_err_pi,
input esc_err_ni,
// normal I/Os
input esc_en_i,
input ping_en_i,
input ping_ok_o,
input integ_fail_o,
input esc_en_o
);
logic error_present;
assign error_present = resp_err_pi | resp_err_ni |
esc_err_pi | esc_err_ni;
// ping will stay high until ping ok received, then it must be deasserted
// TODO: this escludes the case where no ping ok will be returned due to an error
`ASSUME_FPV(PingDeassert_M, ping_en_i && ping_ok_o |=> ping_en_i, clk_i, !rst_ni)
`ASSUME_FPV(PingEnStaysAsserted0_M, ping_en_i |=>
(ping_en_i && !ping_ok_o) or (ping_en_i && ping_ok_o ##1 $fell(ping_en_i)),
clk_i, !rst_ni || error_present)
// assume that the ping enable and escalation enable signals will eventually be deasserted (and
// esc will stay low for more than 2 cycles)
`ASSUME_FPV(FiniteEsc_M, esc_en_i |-> strong(##[1:$] !esc_en_i [*2]))
`ASSUME_FPV(FinitePing_M, ping_en_i |-> strong(##[1:$] !ping_en_i))
// ping response mus occur within 4 cycles (given that no
// error occured within the previous cycles)
`ASSERT(PingRespCheck_A, !error_present [*4] ##1 $rose(ping_en_i) |->
##[0:4] ping_ok_o, clk_i, !rst_ni || error_present)
// be more specific (i.e. use throughout)
`ASSERT(EscRespCheck_A, ##1 esc_en_i |-> ##[0:1] prim_esc_rxtx_fpv.esc_rx_out.resp_p ##1
!prim_esc_rxtx_fpv.esc_rx_out.resp_p, clk_i, !rst_ni || error_present)
// check correct transmission of escalation within 0-1 cycles
`ASSERT(EscCheck_A, ##1 esc_en_i |-> ##[0:1] esc_en_o, clk_i, !rst_ni || error_present)
// check that a single error on the diffpairs is detected
`ASSERT(SingleSigIntDetected0_A, {esc_err_pi, esc_err_ni} == '0 ##1
$onehot({resp_err_pi, resp_err_ni}) |-> integ_fail_o)
`ASSERT(SingleSigIntDetected1_A, $onehot({esc_err_pi, esc_err_ni}) ##1
{resp_err_pi, resp_err_ni} == '0 |-> integ_fail_o)
// basic liveness of FSMs in case no errors are present
`ASSERT(FsmLivenessSender_A, (prim_esc_rxtx_fpv.i_prim_esc_sender.state_q !=
prim_esc_rxtx_fpv.i_prim_esc_sender.Idle) |->
strong(##[1:$] (prim_esc_rxtx_fpv.i_prim_esc_sender.state_q
== prim_esc_rxtx_fpv.i_prim_esc_sender.Idle)), clk_i, !rst_ni || error_present)
`ASSERT(FsmLivenessReceiver_A, (prim_esc_rxtx_fpv.i_prim_esc_receiver.state_q !=
prim_esc_rxtx_fpv.i_prim_esc_receiver.Idle) |->
strong(##[1:$] (prim_esc_rxtx_fpv.i_prim_esc_receiver.state_q
== prim_esc_rxtx_fpv.i_prim_esc_receiver.Idle)), clk_i, !rst_ni || error_present)
endmodule : prim_esc_rxtx_assert_fpv

View file

@ -0,0 +1,213 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Assertions for prim_fifo_sync.
// Intended to be used with a formal tool.
`include "prim_assert.sv"
module prim_fifo_sync_assert_fpv #(
// can be desabled for deeper FIFOs
parameter bit EnableDataCheck = 1'b1,
parameter int unsigned Width = 16,
parameter bit Pass = 1'b1,
parameter int unsigned Depth = 4,
localparam int unsigned DepthWNorm = $clog2(Depth+1),
localparam int unsigned DepthW = (DepthWNorm == 0) ? 1 : DepthWNorm
) (
input clk_i,
input rst_ni,
input clr_i,
input wvalid,
input wready,
input [Width-1:0] wdata,
input rvalid,
input rready,
input [Width-1:0] rdata,
input [DepthW-1:0] depth
);
/////////////////
// Assumptions //
/////////////////
// no need to consider all possible input words
// 2-3 different values suffice
`ASSUME(WdataValues_M, wdata inside {Width'(1'b0), Width'(1'b1), {Width{1'b1}}}, clk_i, !rst_ni)
////////////////////////////////
// Data and Depth Value Check //
////////////////////////////////
if (EnableDataCheck && Depth > 0) begin : gen_data_check
logic [DepthW+2:0] ref_depth;
logic [Width-1:0] ref_rdata;
// no pointers needed in this case
if (Depth == 1) begin : gen_no_ptrs
logic [Width-1:0] fifo;
logic [DepthW+2:0] wptr, rptr;
// this only models the data flow, since the control logic is tested below
always_ff @(posedge clk_i or negedge rst_ni) begin : p_fifo_model
if (!rst_ni) begin
ref_depth <= 0;
end else begin
if (clr_i) begin
ref_depth <= 0;
end else begin
if (wvalid && wready && rvalid && rready) begin
fifo <= wdata;
end else if (wvalid && wready) begin
fifo <= wdata;
ref_depth <= ref_depth + 1;
end else if (rvalid && rready) begin
ref_depth <= ref_depth - 1;
end
end
end
end
if (Pass) begin : gen_pass
assign ref_rdata = (ref_depth == 0) ? wdata : fifo;
end else begin : no_pass
assign ref_rdata = fifo;
end
// general case
end else begin : gen_ptrs
logic [Width-1:0] fifo [Depth];
logic [DepthW+2:0] wptr, rptr;
// implements (++val) mod Depth
function automatic logic [DepthW+2:0] modinc(logic [DepthW+2:0] val, int modval);
if (val == Depth-1) return 0;
else return val + 1;
endfunction
// this only models the data flow, since the control logic is tested below
always_ff @(posedge clk_i or negedge rst_ni) begin : p_fifo_model
if (!rst_ni) begin
wptr <= 0;
rptr <= 0;
ref_depth <= 0;
end else begin
if (clr_i) begin
wptr <= 0;
rptr <= 0;
ref_depth <= 0;
end else begin
if (wvalid && wready && rvalid && rready) begin
fifo[wptr] <= wdata;
wptr <= modinc(wptr, Depth);
rptr <= modinc(rptr, Depth);
end else if (wvalid && wready) begin
fifo[wptr] <= wdata;
wptr <= modinc(wptr, Depth);
ref_depth <= ref_depth + 1;
end else if (rvalid && rready) begin
rptr <= modinc(rptr, Depth);
ref_depth <= ref_depth - 1;
end
end
end
end
if (Pass) begin : gen_pass
assign ref_rdata = (ref_depth == 0) ? wdata : fifo[rptr];
end else begin : no_pass
assign ref_rdata = fifo[rptr];
end
end
// check the data
`ASSERT(DataCheck_A, rvalid |-> rdata == ref_rdata)
// check the depth
`ASSERT(DepthCheck_A, ref_depth == depth)
end
////////////////////////
// Forward Assertions //
////////////////////////
// assert depth of FIFO
`ASSERT(Depth_A, depth <= Depth)
// if we clear the FIFO, it must be empty in the next cycle
`ASSERT(CheckClrDepth_A, clr_i |=> depth == 0)
// check write on full
`ASSERT(WriteFull_A, depth == Depth && wvalid && !rready |=> depth == $past(depth),
clk_i, !rst_ni || clr_i)
// read empty
`ASSERT(ReadEmpty_A, depth == 0 && rready && !wvalid |=> depth == 0,
clk_i, !rst_ni || clr_i)
// this is unreachable in depth 1 no-pass through mode
if (Depth == 1 && Pass) begin : gen_d1_passthru
// check simultaneous write and read
`ASSERT(WriteAndRead_A, wready && wvalid && rvalid && rready |=> depth == $past(depth),
clk_i, !rst_ni || clr_i)
end
if (Depth == 0) begin : gen_depth0
// if there is no register, the FIFO is per definition pass-through
`ASSERT_INIT(ZeroDepthNeedsPass_A, Pass == 1)
// depth must remain zero
`ASSERT(DepthAlwaysZero_A, depth == 0)
// data is just passed through
`ASSERT(DataPassThru_A, wdata == rdata)
// FIFO is ready if downstream logic is ready
`ASSERT(Wready_A, rready == wready)
// valid input is valid output
`ASSERT(Rvalid_A, rvalid == wvalid)
// ensure full coverage
`ASSERT(UnusedClr_A, prim_fifo_sync.gen_passthru_fifo.unused_clr == clr_i)
end else begin : gen_depth_gt0
// check wready
`ASSERT(Wready_A, depth < Depth |-> wready)
// check rvalid
`ASSERT(Rvalid_A, depth > 0 |-> rvalid)
// check write only
`ASSERT(WriteOnly_A, wvalid && wready && !rready && depth < Depth |=>
depth == $past(depth) + 1, clk_i, !rst_ni || clr_i)
// check read only
`ASSERT(ReadOnly_A, !wvalid && rready && rvalid && depth > 0 |=>
depth == $past(depth) - 1, clk_i, !rst_ni || clr_i)
end
if (Pass) begin : gen_pass_fwd
// if we clear the FIFO, it must be empty in the next cycle
// but we may also get a pass through
`ASSERT(CheckClrValid_A, clr_i |=> wvalid == rvalid)
end else begin : gen_nopass_fwd
// if we clear the FIFO, it must be empty in the next cycle
`ASSERT(CheckClrValid_A, clr_i |=> !rvalid)
end
/////////////////////////
// Backward Assertions //
/////////////////////////
if (Pass) begin : gen_pass_bkwd
// there is still space in the FIFO or downstream logic is ready
`ASSERT(WreadySpacekBkwd_A, wready |-> depth < Depth || rready)
// elements ready to be read or upstream data is valid
`ASSERT(RvalidElemskBkwd_A, rvalid |-> depth > 0 || wvalid)
end else begin : gen_nopass_bkwd
// there is still space in the FIFO
`ASSERT(WreadySpacekBkwd_A, wready |-> depth < Depth)
// elements ready to be read
`ASSERT(RvalidElemskBkwd_A, rvalid |-> depth > 0)
end
// no more space in the FIFO
`ASSERT(WreadyNoSpaceBkwd_A, !wready |-> depth == Depth)
// elements ready to be read
`ASSERT(RvalidNoElemskBkwd_A, !rvalid |-> depth == 0)
endmodule : prim_fifo_sync_assert_fpv

4
vendor/lowrisc_ip/prim/lint/prim.vlt vendored Normal file
View file

@ -0,0 +1,4 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//

63
vendor/lowrisc_ip/prim/lint/prim.waiver vendored Normal file
View file

@ -0,0 +1,63 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
#
# waiver file for prim
# prim_fifo_sync
waive -rules {ONE_BIT_MEM_WIDTH} -location {prim_fifo_sync.sv} -msg {Memory 'gen_normal_fifo.storage' has word width which is single bit wide} \
-comment "It is permissible that a FIFO has a wordwidth of 1bit"
# prim_fifo_async
waive -rules {ASSIGN_SIGN} -location {prim_fifo_async.sv} -msg {Signed target 'i' assigned unsigned value 'PTR_WIDTH - 3'} \
-comment "Parameter PTR_WIDTH is unsigned, but integer i is signed. This is fine. Changing the integer to unsigned might \
cause issues with the for loop never exiting, because an unsigned integer can never become < 0."
# prim_assert
waive -rules {UNDEF_MACRO_REF} -location {prim_assert.sv} -regexp {Macro definition for 'ASSERT_RPT' includes expansion of undefined macro '__(FILE|LINE)__'} \
-comment "This is an UVM specific macro inside our assertion shortcuts"
# unfortunately most tools do not support line wrapping within the declaration of macro functions, hence we have to waive
# line length violations.
waive -rules {LINE_LENGTH} -location {prim_assert.sv} -msg {Line length of} \
-comment "Some macros cannot be line-wrapped, as some tools do not support that."
# prim_packer
waive -rules INTEGER -location {prim_packer.sv} -msg {'i' of type int used as a non-constant value} \
-comment "This assigns int i (signed) to a multibit logic variable (unsigned), which is fine"
# primitives: prim_subreg
waive -rules INPUT_NOT_READ -location {prim_subreg.sv} -regexp {Input port 'wd' is not read from} \
-comment "for RO wd is not used"
# primitives: prim_arbiter_*
waive -rules PARTIAL_CONST_ASSIGN -location {prim_arbiter_*.sv} -regexp {'mask.0.' is conditionally assigned a constant} \
-comment "makes the code more readable"
waive -rules CONST_FF -location {prim_arbiter_*.sv} -regexp {Flip-flop 'mask.0.' is driven by constant} \
-comment "makes the code more readable"
# primitives: prim_sram_arbiter
waive -rules CONST_OUTPUT -location {prim_sram_arbiter.sv} -regexp {rsp_error.* is driven by constant} \
-comment "SRAM protection is not yet implemented"
# primitives: prim_fifos
waive -rules VAR_INDEX_RANGE -location {prim_fifo_*sync.sv} -regexp {maximum value .* may be too large for 'storage'} \
-comment "index is protected by control logic"
waive -rules EXPLICIT_BITLEN -location {prim_fifo_*sync.sv} -regexp {Bit length not specified for constant '1'} \
-comment "index is protected by control logic"
waive -rules NOT_READ -location {prim_fifo_async.sv} -regexp {Signal 'nc_decval_msb' is not read} \
-comment "Store temporary values. Not used intentionally"
waive -rules {INPUT_NOT_READ} -location {prim_fifo_sync.sv} -regexp {Input port '(clk_i|rst_ni)' is not read from, instance.*Depth=0\)} \
-comment "In passthrough mode, clk and reset are not read form within this module"
# TL-UL fifo
waive -rules {HIER_BRANCH_NOT_READ} -location {tlul_fifo_sync.sv} -regexp {Connected net '(clk_i|rst_ni)' at prim_fifo_sync.sv:.* is not read from in module 'prim_fifo_sync'} \
-comment "In passthrough mode, clk and reset are not read form within this module"
# primitivies: prim_ram_2p_wrapper
#
#waive -rules INPUT_NOT_READ -location {prim_ram_*_wrapper*} -regexp {cfg_i} \
# -comment "Register model doesn't need config port"
#waive -rules NOT_READ -location {prim_ram_*_wrapper*} -regexp {(a|b)_rdata_(q|d)\[38} \
# -comment "Syndrome is not going out to the interface"

View file

@ -0,0 +1,34 @@
REQ/ACK Syncronizer Verilator Testbench
=======================================
This directory contains a basic, scratch Verilator testbench targeting
functional verification of the REQ/ACK synchronizer primitive during
development.
How to build and run the testbench
----------------------------------
From the OpenTitan top level execute
```sh
fusesoc --cores-root=. run --setup --build \
lowrisc:dv_verilator:prim_sync_reqack_tb
```
to build the testbench and afterwards
```sh
./build/lowrisc_dv_verilator_prim_sync_reqack_tb_0/default-verilator/Vprim_sync_reqack_tb \
--trace
```
to run it.
Details of the testbench
------------------------
- `rtl/prim_sync_reqack_tb.sv`: SystemVerilog testbench, instantiates and
drives the DUT, counts handshakes in both domains, signals test end and
result (pass/fail) to C++ via output ports. Change this file to e.g.
for a different clock ratio or more transactions.
- `cpp/prim_sync_reqack_tb.cc`: Contains main function and instantiation of
SimCtrl, reads output ports of DUT and signals simulation termination to
Verilator.

View file

@ -0,0 +1,62 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "Vprim_sync_reqack_tb.h"
#include "verilated_toplevel.h"
#include "verilator_sim_ctrl.h"
#include <signal.h>
#include <functional>
#include <iostream>
#include "sim_ctrl_extension.h"
class PrimSyncReqAckTB : public SimCtrlExtension {
using SimCtrlExtension::SimCtrlExtension;
public:
PrimSyncReqAckTB(prim_sync_reqack_tb *top);
void OnClock(unsigned long sim_time);
private:
prim_sync_reqack_tb *top_;
};
// Constructor:
// - Set up top_ ptr
PrimSyncReqAckTB::PrimSyncReqAckTB(prim_sync_reqack_tb *top)
: SimCtrlExtension{}, top_(top) {}
// Function called once every clock cycle from SimCtrl
void PrimSyncReqAckTB::OnClock(unsigned long sim_time) {
if (top_->test_done_o) {
VerilatorSimCtrl::GetInstance().RequestStop(top_->test_passed_o);
}
}
int main(int argc, char **argv) {
int ret_code;
// Init verilog instance
prim_sync_reqack_tb top;
// Init sim
VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
simctrl.SetTop(&top, &top.clk_i, &top.rst_ni,
VerilatorSimCtrlFlags::ResetPolarityNegative);
// Create and register VerilatorSimCtrl extension
PrimSyncReqAckTB primsyncreqacktb(&top);
simctrl.RegisterExtension(&primsyncreqacktb);
std::cout << "Simulation of REQ/ACK Synchronizer primitive" << std::endl
<< "============================================" << std::endl
<< std::endl;
// Get pass / fail from Verilator
ret_code = simctrl.Exec(argc, argv);
return ret_code;
}

View file

@ -0,0 +1,52 @@
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:dv_verilator:prim_sync_reqack_tb"
description: "REQ/ACK Synchronizer Verilator TB"
filesets:
files_rtl:
depend:
- lowrisc:prim:all
files:
- rtl/prim_sync_reqack_tb.sv
file_type: systemVerilogSource
files_dv_verilator:
depend:
- lowrisc:dv_verilator:simutil_verilator
files:
- cpp/prim_sync_reqack_tb.cc
file_type: cppSource
targets:
default:
default_tool: verilator
filesets:
- files_rtl
- files_dv_verilator
toplevel: prim_sync_reqack_tb
tools:
verilator:
mode: cc
verilator_options:
# Disabling tracing reduces compile times by multiple times, but doesn't have a
# huge influence on runtime performance. (Based on early observations.)
- '--trace'
- '--trace-fst' # this requires -DVM_TRACE_FMT_FST in CFLAGS below!
- '--trace-structs'
- '--trace-params'
- '--trace-max-array 1024'
# compiler flags
#
# -O
# Optimization levels have a large impact on the runtime performance of the
# simulation model. -O2 and -O3 are pretty similar, -Os is slower than -O2/-O3
- '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=prim_sync_reqack_tb -g -O0"'
- '-LDFLAGS "-pthread -lutil -lelf"'
- "-Wall"
- "-Wno-PINCONNECTEMPTY"
# XXX: Cleanup all warnings and remove this option
# (or make it more fine-grained at least)
- "-Wno-fatal"

View file

@ -0,0 +1,173 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Scratch verification testbench for REQ/ACK synchronizer primitive
module prim_sync_reqack_tb #(
) (
input logic clk_i,
input logic rst_ni,
output logic test_done_o,
output logic test_passed_o
);
// TB configuration
localparam int unsigned NumTransactions = 8;
localparam logic FastToSlow = 1'b1; // Select 1'b0 for SlowToFast
localparam int unsigned Ratio = 4; // must be even and greater equal 2
// Derivation of parameters
localparam int unsigned Ticks = Ratio/2;
localparam int unsigned WidthTicks = $clog2(Ticks)+1;
localparam int unsigned WidthTrans = $clog2(NumTransactions)+1;
// Derive slow clock (using a counter)
logic [WidthTicks-1:0] count_clk_d, count_clk_q;
assign count_clk_d = count_clk_q == (Ticks[WidthTicks-1:0]-1) ? '0 : count_clk_q + {{WidthTicks-1{1'b0}},{1'b1}};
always_ff @(posedge clk_i) begin : reg_count_clk
count_clk_q <= count_clk_d;
end
logic clk_slow_d, clk_slow_q, clk_slow;
assign clk_slow_d = count_clk_q == (Ticks[WidthTicks-1:0]-1) ? !clk_slow_q : clk_slow_q;
always_ff @(posedge clk_i) begin : reg_clk_slow
clk_slow_q <= clk_slow_d;
end
assign clk_slow = clk_slow_q;
// Sync reset to slow clock
logic [1:0] rst_slow_nq;
logic rst_slow_n;
always_ff @(posedge clk_slow) begin
rst_slow_nq <= {rst_slow_nq[0], rst_ni};
end
assign rst_slow_n = rst_ni & rst_slow_nq[1];
// Connect clocks
logic clk_src, clk_dst;
assign clk_src = FastToSlow ? clk_i : clk_slow;
assign clk_dst = FastToSlow ? clk_slow : clk_i;
logic src_req, dst_req;
logic src_ack, dst_ack;
logic rst_done;
// Instantiate DUT
prim_sync_reqack prim_sync_reqack (
.clk_src_i (clk_src),
.rst_src_ni (rst_slow_n),
.clk_dst_i (clk_dst),
.rst_dst_ni (rst_slow_n),
.src_req_i (src_req),
.src_ack_o (src_ack),
.dst_req_o (dst_req),
.dst_ack_i (dst_ack)
);
// Make sure we do not apply stimuli before the reset.
always_ff @(posedge clk_slow or negedge rst_slow_n) begin
if (!rst_slow_n) begin
rst_done <= '1;
end else begin
rst_done <= rst_done;
end
end
// Create randomized ACK delay
localparam int WIDTH_COUNT = 3;
logic [31:0] tmp;
logic [31-WIDTH_COUNT:0] unused_tmp;
assign unused_tmp = tmp[31:WIDTH_COUNT];
logic [WIDTH_COUNT-1:0] dst_count_clk_d, dst_count_clk_q;
logic [WIDTH_COUNT-1:0] dst_count_clk_max_d, dst_count_clk_max_q;
logic count_exp;
assign count_exp = dst_count_clk_q == dst_count_clk_max_q;
always_comb begin
dst_count_clk_d = dst_count_clk_q;
dst_count_clk_max_d = dst_count_clk_max_q;
tmp = '0;
if (dst_req && count_exp) begin
// Clear counter
dst_count_clk_d = '0;
// Get new max
tmp = $random;
dst_count_clk_max_d = tmp[2:0];
end else if (dst_req) begin
// Increment
dst_count_clk_d = dst_count_clk_q + {{WIDTH_COUNT-1{1'b0}},{1'b1}};
end
end
always_ff @(posedge clk_dst or negedge rst_slow_n) begin : reg_dst_count_clk
if (!rst_slow_n) begin
dst_count_clk_q <= '0;
dst_count_clk_max_q <= '0;
end else begin
dst_count_clk_q <= dst_count_clk_d;
dst_count_clk_max_q <= dst_count_clk_max_d;
end
end
// Apply stimuli
always_comb begin
src_req = 1'b0;
dst_ack = 1'b0;
if (rst_done && rst_slow_n) begin
// The source wants to perform handshakes at maximum rate.
src_req = 1'b1;
end
if (dst_req && count_exp) begin
// The destination sends the ACK after a random delay.
dst_ack = 1'b1;
end
end
// Count handshakes on both sides
logic [WidthTrans-1:0] src_count_d, src_count_q;
logic [WidthTrans-1:0] dst_count_d, dst_count_q;
assign src_count_d = (src_req && src_ack) ? src_count_q + 1'b1 : src_count_q;
always_ff @(posedge clk_src or negedge rst_slow_n) begin : reg_src_count
if (!rst_slow_n) begin
src_count_q <= '0;
end else begin
src_count_q <= src_count_d;
end
end
assign dst_count_d = (dst_req && dst_ack) ? dst_count_q + 1'b1 : dst_count_q;
always_ff @(posedge clk_dst or negedge rst_slow_n) begin : reg_dst_count
if (!rst_slow_n) begin
dst_count_q <= '0;
end else begin
dst_count_q <= dst_count_d;
end
end
// Check responses, signal end of simulation
always_ff @(posedge clk_i) begin : tb_ctrl
test_done_o <= 1'b0;
test_passed_o <= 1'b1;
if ((src_count_q == NumTransactions[WidthTrans-1:0]) &&
(dst_count_q == NumTransactions[WidthTrans-1:0])) begin // Success
$display("\nSUCCESS: Performed %0d handshakes in both source and destination domain.",
NumTransactions);
$display("Finishing simulation now.\n");
test_passed_o <= 1'b1;
test_done_o <= 1'b1;
end else if (((src_count_q > dst_count_q) && ((src_count_q - dst_count_q) > 1)) ||
((dst_count_q > src_count_q) && ((dst_count_q - src_count_q) > 1))) begin // Failed
$display("\nERROR: Performed %0d handshakes in source domain, and %0d in destination domain.",
src_count_q, dst_count_q);
$display("Finishing simulation now.\n");
test_passed_o <= 1'b0;
test_done_o <= 1'b1;
end
end
endmodule

80
vendor/lowrisc_ip/prim/prim.core vendored Normal file
View file

@ -0,0 +1,80 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
#
# XXX: Split this core into multiple smaller ones.
name: "lowrisc:prim:all:0.1"
description: "Primitives"
filesets:
files_rtl:
depend:
- lowrisc:prim:ram_2p # for prim_ram_2p_adv
- lowrisc:prim:secded # for prim_ram_2p_adv
- lowrisc:prim:assert
- lowrisc:prim:diff_decode # for prim_alert_sender/receiver
- lowrisc:prim:pad_wrapper
- lowrisc:prim:prim_pkg
- lowrisc:prim:clock_mux2
files:
- rtl/prim_clock_inverter.sv
- rtl/prim_clock_gating_sync.sv
- rtl/prim_alert_pkg.sv
- rtl/prim_alert_receiver.sv
- rtl/prim_alert_sender.sv
- rtl/prim_arbiter_ppc.sv
- rtl/prim_arbiter_tree.sv
- rtl/prim_esc_pkg.sv
- rtl/prim_esc_receiver.sv
- rtl/prim_esc_sender.sv
- rtl/prim_sram_arbiter.sv
- rtl/prim_fifo_async.sv
- rtl/prim_fifo_sync.sv
- rtl/prim_flop_2sync.sv
- rtl/prim_sync_reqack.sv
- rtl/prim_keccak.sv
- rtl/prim_lfsr.sv
- rtl/prim_packer.sv
- rtl/prim_cipher_pkg.sv
- rtl/prim_present.sv
- rtl/prim_prince.sv
- rtl/prim_gate_gen.sv
- rtl/prim_pulse_sync.sv
- rtl/prim_filter.sv
- rtl/prim_filter_ctr.sv
- rtl/prim_subreg.sv
- rtl/prim_subreg_ext.sv
- rtl/prim_intr_hw.sv
- rtl/prim_ram_2p_adv.sv
- rtl/prim_ram_2p_async_adv.sv
file_type: systemVerilogSource
files_verilator_waiver:
depend:
# common waivers
- lowrisc:lint:common
files:
- lint/prim.vlt
file_type: vlt
files_ascentlint_waiver:
depend:
# common waivers
- lowrisc:lint:common
files:
- lint/prim.waiver
file_type: waiver
files_veriblelint_waiver:
depend:
# common waivers
- lowrisc:lint:common
- lowrisc:lint:comportable
targets:
default:
filesets:
- tool_verilator ? (files_verilator_waiver)
- tool_ascentlint ? (files_ascentlint_waiver)
- tool_veriblelint ? (files_veriblelint_waiver)
- files_rtl

View file

@ -0,0 +1,25 @@
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:clock_gating"
description: "Clock gating primitives"
filesets:
primgen_dep:
depend:
- lowrisc:prim:prim_pkg
- lowrisc:prim:primgen
generate:
impl:
generator: primgen
parameters:
prim_name: clock_gating
targets:
default:
filesets:
- primgen_dep
generate:
- impl

View file

@ -0,0 +1,25 @@
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:clock_mux2"
description: "2-input clock multiplexer"
filesets:
primgen_dep:
depend:
- lowrisc:prim:prim_pkg
- lowrisc:prim:primgen
generate:
impl:
generator: primgen
parameters:
prim_name: clock_mux2
targets:
default:
filesets:
- primgen_dep
generate:
- impl

View file

@ -3,12 +3,14 @@ CAPI=2:
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:prim:lfsr:0.1"
description: "LFSR primitive"
name: "lowrisc:prim:diff_decode"
description: "prim"
filesets:
files_rtl:
depend:
- lowrisc:prim:assert
files:
- rtl/prim_lfsr.sv
- rtl/prim_diff_decode.sv
file_type: systemVerilogSource
targets:

25
vendor/lowrisc_ip/prim/prim_flash.core vendored Normal file
View file

@ -0,0 +1,25 @@
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:flash"
description: "Flash memory"
filesets:
primgen_dep:
depend:
- lowrisc:prim:prim_pkg
- lowrisc:prim:primgen
generate:
impl:
generator: primgen
parameters:
prim_name: flash
targets:
default:
filesets:
- primgen_dep
generate:
- impl

View file

@ -3,12 +3,15 @@ CAPI=2:
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:prim:ram_1p:0.1"
description: "Single port RAM (technology independent)"
name: "lowrisc:prim:gf_mult"
description: "Galois multiplier"
filesets:
files_rtl:
depend:
- lowrisc:prim_generic:ram_1p
- lowrisc:prim:assert
files:
- rtl/prim_gf_mult.sv
file_type: systemVerilogSource
targets:
default:

25
vendor/lowrisc_ip/prim/prim_otp.core vendored Normal file
View file

@ -0,0 +1,25 @@
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:otp"
description: "One-Time Programmable (OTP) memory"
filesets:
primgen_dep:
depend:
- lowrisc:prim:prim_pkg
- lowrisc:prim:primgen
generate:
impl:
generator: primgen
parameters:
prim_name: otp
targets:
default:
filesets:
- primgen_dep
generate:
- impl

View file

@ -0,0 +1,25 @@
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:pad_wrapper"
description: "PAD wrapper"
filesets:
primgen_dep:
depend:
- lowrisc:prim:prim_pkg
- lowrisc:prim:primgen
generate:
impl:
generator: primgen
parameters:
prim_name: pad_wrapper
targets:
default:
filesets:
- primgen_dep
generate:
- impl

23
vendor/lowrisc_ip/prim/prim_pkg.core vendored Normal file
View file

@ -0,0 +1,23 @@
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:prim_pkg:0.1"
description: "Constants used by the primitives"
filesets:
primgen_dep:
depend:
- lowrisc:prim:primgen
generate:
impl:
generator: primgen
parameters:
action: generate_prim_pkg
targets:
default:
filesets:
- primgen_dep
generate:
- impl

25
vendor/lowrisc_ip/prim/prim_ram_1p.core vendored Normal file
View file

@ -0,0 +1,25 @@
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:ram_1p"
description: "1 port random-access memory"
filesets:
primgen_dep:
depend:
- lowrisc:prim:prim_pkg
- lowrisc:prim:primgen
generate:
impl:
generator: primgen
parameters:
prim_name: ram_1p
targets:
default:
filesets:
- primgen_dep
generate:
- impl

View file

@ -0,0 +1,19 @@
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:ram_1p_adv:0.1"
description: "Single-port RAM primitive with advanced features"
filesets:
files_rtl:
depend:
- lowrisc:prim:ram_1p
files:
- rtl/prim_ram_1p_adv.sv
file_type: systemVerilogSource
targets:
default:
filesets:
- files_rtl

25
vendor/lowrisc_ip/prim/prim_ram_2p.core vendored Normal file
View file

@ -0,0 +1,25 @@
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:ram_2p"
description: "2 port random-access memory"
filesets:
primgen_dep:
depend:
- lowrisc:prim:prim_pkg
- lowrisc:prim:primgen
generate:
impl:
generator: primgen
parameters:
prim_name: ram_2p
targets:
default:
filesets:
- primgen_dep
generate:
- impl

25
vendor/lowrisc_ip/prim/prim_rom.core vendored Normal file
View file

@ -0,0 +1,25 @@
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:rom"
description: "Read-only memory (ROM)"
filesets:
primgen_dep:
depend:
- lowrisc:prim:prim_pkg
- lowrisc:prim:primgen
generate:
impl:
generator: primgen
parameters:
prim_name: rom
targets:
default:
filesets:
- primgen_dep
generate:
- impl

View file

@ -10,6 +10,8 @@ filesets:
files:
- rtl/prim_secded_28_22_dec.sv
- rtl/prim_secded_28_22_enc.sv
- rtl/prim_secded_39_32_dec.sv
- rtl/prim_secded_39_32_enc.sv
- rtl/prim_secded_72_64_dec.sv
- rtl/prim_secded_72_64_enc.sv
file_type: systemVerilogSource

View file

@ -0,0 +1,17 @@
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:util_memload"
description: "Memory loaders for simulation. To be included in a memory primitive."
filesets:
files_rtl:
files:
- rtl/prim_util_memload.sv: {is_include_file: true}
file_type: systemVerilogSource
targets:
default:
filesets:
- files_rtl

10
vendor/lowrisc_ip/prim/primgen.core vendored Normal file
View file

@ -0,0 +1,10 @@
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:primgen:0.1"
generators:
primgen:
interpreter: python3
command: util/primgen.py

View file

@ -0,0 +1,17 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
package prim_alert_pkg;
typedef struct packed {
logic alert_p;
logic alert_n;
} alert_tx_t;
typedef struct packed {
logic ping_p;
logic ping_n;
logic ack_p;
logic ack_n;
} alert_rx_t;
endpackage

View file

@ -0,0 +1,214 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// The alert receiver primitive decodes alerts that have been differentially
// encoded and transmitted via a handshake protocol on alert_p/n and
// ack_p/n. In case an alert handshake is initiated, the output alert_o will
// immediately be asserted (even before completion of the handshake).
//
// In case the differential input is not correctly encoded, this module will
// raise an error by asserting integ_fail_o.
//
// Further, the module supports ping testing of the alert diff pair. In order to
// initiate a ping test, ping_en_i shall be set to 1'b1 until ping_ok_o is
// asserted for one cycle. The signal may be de-asserted (e.g. after a long)
// timeout period. However note that all ping responses that come in after
// deasserting ping_en_i will be treated as native alerts.
//
// The protocol works in both asynchronous and synchronous cases. In the
// asynchronous case, the parameter AsyncOn must be set to 1'b1 in order to
// instantiate additional synchronization logic. Further, it must be ensured
// that the timing skew between all diff pairs is smaller than the shortest
// clock period of the involved clocks.
//
// Note that in case of synchronous operation, alerts on the diffpair are
// decoded combinationally and forwarded on alert_o within the same cycle.
//
// See also: prim_alert_sender, prim_diff_decode, alert_handler
`include "prim_assert.sv"
module prim_alert_receiver
import prim_alert_pkg::*;
#(
// enables additional synchronization logic
parameter bit AsyncOn = 1'b0
) (
input clk_i,
input rst_ni,
// this triggers a ping test. keep asserted
// until ping_ok_o is asserted.
input ping_en_i,
output logic ping_ok_o,
// asserted if signal integrity issue detected
output logic integ_fail_o,
// alert output (pulsed high) if a handshake is initiated
// on alert_p/n and no ping request is outstanding
output logic alert_o,
// ping input diff pair and ack diff pair
output alert_rx_t alert_rx_o,
// alert output diff pair
input alert_tx_t alert_tx_i
);
/////////////////////////////////
// decode differential signals //
/////////////////////////////////
logic alert_level, alert_sigint;
prim_diff_decode #(
.AsyncOn(AsyncOn)
) i_decode_alert (
.clk_i,
.rst_ni,
.diff_pi ( alert_tx_i.alert_p ),
.diff_ni ( alert_tx_i.alert_n ),
.level_o ( alert_level ),
.rise_o ( ),
.fall_o ( ),
.event_o ( ),
.sigint_o ( alert_sigint )
);
/////////////////////////////////////////////////////
// main protocol FSM that drives the diff outputs //
/////////////////////////////////////////////////////
typedef enum logic [1:0] {Idle, HsAckWait, Pause0, Pause1} state_e;
state_e state_d, state_q;
logic ping_rise;
logic ping_tog_d, ping_tog_q, ack_d, ack_q;
logic ping_en_d, ping_en_q;
logic ping_pending_d, ping_pending_q;
// signal ping request upon positive transition on ping_en_i
// signalling is performed by a level change event on the diff output
assign ping_en_d = ping_en_i;
assign ping_rise = ping_en_i && !ping_en_q;
assign ping_tog_d = (ping_rise) ? ~ping_tog_q : ping_tog_q;
// the ping pending signal is used to in the FSM to distinguish whether the
// incoming handshake shall be treated as an alert or a ping response.
// it is important that this is only set on a rising ping_en level change, since
// otherwise the ping enable signal could be abused to "mask" all native alerts
// as ping responses by constantly tying it to 1.
assign ping_pending_d = ping_rise | ((~ping_ok_o) & ping_en_i & ping_pending_q);
// diff pair outputs
assign alert_rx_o.ack_p = ack_q;
assign alert_rx_o.ack_n = ~ack_q;
assign alert_rx_o.ping_p = ping_tog_q;
assign alert_rx_o.ping_n = ~ping_tog_q;
// this FSM receives the four phase handshakes from the alert receiver
// note that the latency of the alert_p/n input diff pair is at least one
// cycle until it enters the receiver FSM. the same holds for the ack_* diff
// pair outputs.
always_comb begin : p_fsm
// default
state_d = state_q;
ack_d = 1'b0;
ping_ok_o = 1'b0;
integ_fail_o = 1'b0;
alert_o = 1'b0;
unique case (state_q)
Idle: begin
// wait for handshake to be initiated
if (alert_level) begin
state_d = HsAckWait;
ack_d = 1'b1;
// signal either an alert or ping received on the output
if (ping_pending_q) begin
ping_ok_o = 1'b1;
end else begin
alert_o = 1'b1;
end
end
end
// waiting for deassertion of alert to complete HS
HsAckWait: begin
if (!alert_level) begin
state_d = Pause0;
end else begin
ack_d = 1'b1;
end
end
// pause cycles between back-to-back handshakes
Pause0: state_d = Pause1;
Pause1: state_d = Idle;
default : ; // full case
endcase
// override in case of sigint
if (alert_sigint) begin
state_d = Idle;
ack_d = 1'b0;
ping_ok_o = 1'b0;
integ_fail_o = 1'b1;
alert_o = 1'b0;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
if (!rst_ni) begin
state_q <= Idle;
ack_q <= 1'b0;
ping_tog_q <= 1'b0;
ping_en_q <= 1'b0;
ping_pending_q <= 1'b0;
end else begin
state_q <= state_d;
ack_q <= ack_d;
ping_tog_q <= ping_tog_d;
ping_en_q <= ping_en_d;
ping_pending_q <= ping_pending_d;
end
end
////////////////
// assertions //
////////////////
// check whether all outputs have a good known state after reset
`ASSERT_KNOWN(PingOkKnownO_A, ping_ok_o)
`ASSERT_KNOWN(IntegFailKnownO_A, integ_fail_o)
`ASSERT_KNOWN(AlertKnownO_A, alert_o)
`ASSERT_KNOWN(PingPKnownO_A, alert_rx_o)
// check encoding of outgoing diffpairs
`ASSERT(PingDiffOk_A, alert_rx_o.ping_p ^ alert_rx_o.ping_n)
`ASSERT(AckDiffOk_A, alert_rx_o.ack_p ^ alert_rx_o.ack_n)
// ping request at input -> need to see encoded ping request
`ASSERT(PingRequest0_A, ##1 $rose(ping_en_i) |=> $changed(alert_rx_o.ping_p))
// ping response implies it has been requested
`ASSERT(PingResponse0_A, ping_ok_o |-> ping_pending_q)
// correctly latch ping request
`ASSERT(PingPending_A, ##1 $rose(ping_en_i) |=> ping_pending_q)
if (AsyncOn) begin : gen_async_assert
// signal integrity check propagation
`ASSERT(SigInt_A, alert_tx_i.alert_p == alert_tx_i.alert_n [*2] |->
##2 integ_fail_o)
// TODO: need to add skewed cases as well, the assertions below assume no skew at the moment
// ping response
`ASSERT(PingResponse1_A, ##1 $rose(alert_tx_i.alert_p) &&
(alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2 state_q == Idle && ping_pending_q |->
ping_ok_o, clk_i, !rst_ni || integ_fail_o)
// alert
`ASSERT(Alert_A, ##1 $rose(alert_tx_i.alert_p) && (alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2
state_q == Idle && !ping_pending_q |-> alert_o, clk_i, !rst_ni || integ_fail_o)
end else begin : gen_sync_assert
// signal integrity check propagation
`ASSERT(SigInt_A, alert_tx_i.alert_p == alert_tx_i.alert_n |-> integ_fail_o)
// ping response
`ASSERT(PingResponse1_A, ##1 $rose(alert_tx_i.alert_p) && state_q == Idle && ping_pending_q |->
ping_ok_o, clk_i, !rst_ni || integ_fail_o)
// alert
`ASSERT(Alert_A, ##1 $rose(alert_tx_i.alert_p) && state_q == Idle && !ping_pending_q |->
alert_o, clk_i, !rst_ni || integ_fail_o)
end
endmodule : prim_alert_receiver

Some files were not shown because too many files have changed in this diff Show more