diff --git a/dv/riscv_compliance/README.md b/dv/riscv_compliance/README.md new file mode 100644 index 00000000..b0220a2e --- /dev/null +++ b/dv/riscv_compliance/README.md @@ -0,0 +1,67 @@ +Ibex simulation for RISC-V Compliance Testing +============================================= + +This directory contains a compiled simulation of Ibex to be used as target +in the [RISC-V Compliance Test](https://github.com/riscv/riscv-compliance). +In addition to Ibex itself, it contains a 64 kB RAM and a memory-mapped helper +module to interact with the software, e.g. to dump out the test signature and to +end the simulation. + +The simulation is designed for Verilator, but can be adapted to other simulators +if needed. + +How to run RISC-V Compliance on Ibex +------------------------------------ + +0. Check your prerequisites + To compile the simulation and run the compliance test suite you need to + have the following tools installed: + - Verilator + - fusesoc + - srecord (for `srec_cat`) + - A RV32 compiler + + On Ubuntu/Debian, install the required tools like this: + + ```sh + sudo apt-get install srecord python3-pip + pip3 install --user -U fusesoc + ``` + + We recommend installing Verilator from source as versions from Linux distributions are often outdated. + See https://www.veripool.org/projects/verilator/wiki/Installing for installation instructions. + +1. Build a simulation of Ibex + + ```sh + cd $IBEX_REPO_BASE + fusesoc --cores-root=. run --target=sim --setup --build lowrisc:ibex:ibex_riscv_compliance --RV32M=0 --RV32E=0 + ``` + + You can use the two compile-time options `--RV32M` and `--RV32E` to + enable/disable the M and E ISA extensions, respectively. + + You can now find the compiled simulation at `build/lowrisc_ibex_ibex_riscv_compliance_0.1/sim-verilator/Vibex_riscv_compliance`. + +2. Get the RISC-V Compliance test suite + + The compliance test suite currently needs workarounds for Ibex. + Get a modified version of it. + + ``` + git clone https://github.com/imphil/riscv-compliance.git + cd riscv-compliance + git checkout ibex + ``` + + +3. Run the test suite + ```sh + cd $RISCV_COMPLIANCE_REPO_BASE + export RISCV_TARGET=ibex + export RISCV_ISA=rv32i + export RISCV_PREFIX=riscv32-unknown-elf- + export TARGET_SIM=path/to/your/Vibex_riscv_compliance + make clean + make + ``` diff --git a/dv/riscv_compliance/ibex_riscv_compliance.cc b/dv/riscv_compliance/ibex_riscv_compliance.cc new file mode 100644 index 00000000..c6957152 --- /dev/null +++ b/dv/riscv_compliance/ibex_riscv_compliance.cc @@ -0,0 +1,93 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include + +#include "Vibex_riscv_compliance.h" +#include "verilated_toplevel.h" +#include "verilator_sim_ctrl.h" + +VERILATED_TOPLEVEL(ibex_riscv_compliance) + +ibex_riscv_compliance* top; +VerilatorSimCtrl* simctrl; + +static void SignalHandler(int sig) { + if (!simctrl) { + return; + } + + switch (sig) { + case SIGINT: + simctrl->RequestStop(); + break; + case SIGUSR1: + if (simctrl->TracingEnabled()) { + simctrl->TraceOff(); + } else { + simctrl->TraceOn(); + } + break; + } +} + +static void SetupSignalHandler() { + struct sigaction sigIntHandler; + + sigIntHandler.sa_handler = SignalHandler; + sigemptyset(&sigIntHandler.sa_mask); + sigIntHandler.sa_flags = 0; + + sigaction(SIGINT, &sigIntHandler, NULL); + sigaction(SIGUSR1, &sigIntHandler, NULL); +} + +/** + * Get the current simulation time + * + * Called by $time in Verilog, converts to double, to match what SystemC does + */ +double sc_time_stamp() { return simctrl->GetTime(); } + +int main(int argc, char** argv) { + int retcode; + top = new ibex_riscv_compliance; + simctrl = new VerilatorSimCtrl(top, top->IO_CLK, top->IO_RST_N, + VerilatorSimCtrlFlags::ResetPolarityNegative); + + SetupSignalHandler(); + + if (!simctrl->ParseCommandArgs(argc, argv, retcode)) { + goto free_return; + } + + std::cout << "Simulation of Ibex" << std::endl + << "==================" << std::endl + << std::endl; + + if (simctrl->TracingPossible()) { + std::cout << "Tracing can be toggled by sending SIGUSR1 to this process:" + << std::endl + << "$ kill -USR1 " << getpid() << std::endl; + } + + // Initialize RAM + simctrl->InitRam("TOP.ibex_riscv_compliance.u_ram"); + + simctrl->Run(); + simctrl->PrintStatistics(); + + if (simctrl->TracingEverEnabled()) { + std::cout << std::endl + << "You can view the simulation traces by calling" << std::endl + << "$ gtkwave " << simctrl->GetSimulationFileName() << std::endl; + } + +free_return: + delete top; + delete simctrl; + return retcode; +} diff --git a/dv/riscv_compliance/ibex_riscv_compliance.core b/dv/riscv_compliance/ibex_riscv_compliance.core new file mode 100644 index 00000000..5f7a316c --- /dev/null +++ b/dv/riscv_compliance/ibex_riscv_compliance.core @@ -0,0 +1,65 @@ +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:ibex_riscv_compliance:0.1" +description: "Ibex simulation for RISC-V compliance testing (using Verilator)" +filesets: + files_sim_verilator: + depend: + - lowrisc:dv_verilator:simutil_verilator + - lowrisc:ibex:ibex_core + + files: + - rtl/ibex_riscv_compliance.sv + - ibex_riscv_compliance.cc: { file_type: cppSource } + - rtl/prim_clock_gating.sv + - rtl/ram_1p.sv + - rtl/bus.sv + - rtl/riscv_testutil.sv + file_type: systemVerilogSource + +parameters: + RV32M: + datatype: int + paramtype: vlogparam + default: 1 + description: Enable the M ISA extension (hardware multiply/divide) [0/1] + RV32E: + datatype: int + paramtype: vlogparam + default: 0 + description: Enable the E ISA extension (reduced register set) [0/1] + +targets: + sim: + default_tool: verilator + filesets: + - files_sim_verilator + parameters: + - RV32M + - RV32E + toplevel: ibex_riscv_compliance + 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 -g -O0"' + - '-LDFLAGS "-pthread -lutil"' + - "-Wall" + - "-Wno-PINCONNECTEMPTY" + # XXX: Cleanup all warnings and remove this option + # (or make it more fine-grained at least) + - "-Wno-fatal" diff --git a/dv/riscv_compliance/rtl/bus.sv b/dv/riscv_compliance/rtl/bus.sv new file mode 100644 index 00000000..4491e9e1 --- /dev/null +++ b/dv/riscv_compliance/rtl/bus.sv @@ -0,0 +1,120 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Simplistic Ibex bus implementation + * + * This module is designed for demo and simulation purposes, do not use it in + * a real-world system. + * + * This implementation doesn't handle the full bus protocol, but makes the + * following simplifying assumptions. + * + * - All devices (slaves) must respond in the next cycle after the request. + * - Host (master) arbitration is strictly priority based. + */ +module bus #( + parameter NrDevices, + parameter NrHosts, + parameter DataWidth = 32, + parameter AddressWidth = 32 +) ( + input clk_i, + input rst_ni, + + // Hosts (masters) + input host_req_i [NrHosts], + output logic host_gnt_o [NrHosts], + + input [AddressWidth-1:0] host_addr_i [NrHosts], + input host_we_i [NrHosts], + input [ DataWidth/8-1:0] host_be_i [NrHosts], + input [ DataWidth-1:0] host_wdata_i [NrHosts], + output logic host_rvalid_o [NrHosts], + output logic [ DataWidth-1:0] host_rdata_o [NrHosts], + output logic host_err_o [NrHosts], + + // Devices (slaves) + output logic device_req_o [NrDevices], + + output logic [AddressWidth-1:0] device_addr_o [NrDevices], + output logic device_we_o [NrDevices], + output logic [ DataWidth/8-1:0] device_be_o [NrDevices], + output logic [ DataWidth-1:0] device_wdata_o [NrDevices], + input device_rvalid_i [NrDevices], + input [ DataWidth-1:0] device_rdata_i [NrDevices], + input device_err_i [NrDevices], + + // Device address map + input [AddressWidth-1:0] cfg_device_addr_base [NrDevices], + input [AddressWidth-1:0] cfg_device_addr_mask [NrDevices] +); + + logic [$clog2(NrHosts)-1:0] host_sel_req, host_sel_resp; + logic [$clog2(NrDevices)-1:0] device_sel_req, device_sel_resp; + + // Master select prio arbiter + always_comb begin + for (integer host = NrHosts - 1; host >= 0; host = host - 1) begin + if (host_req_i[host]) begin + host_sel_req = $clog2(NrHosts)'(host); + end + end + end + + // Device select + always_comb begin + for (integer device = 0; device < NrDevices; device = device + 1) begin + if ((host_addr_i[host_sel_req] & cfg_device_addr_mask[device]) + == cfg_device_addr_base[device]) begin + device_sel_req = $clog2(NrDevices)'(device); + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + host_sel_resp <= '0; + device_sel_resp <= '0; + end else begin + // Responses are always expected 1 cycle after the request + device_sel_resp <= device_sel_req; + host_sel_resp <= host_sel_req; + end + end + + always_comb begin + for (integer device = 0; device < NrDevices; device = device + 1) begin + if ($clog2(NrDevices)'(device) == device_sel_req) begin + device_req_o[device] = host_req_i[host_sel_req]; + device_we_o[device] = host_we_i[host_sel_req]; + device_addr_o[device] = host_addr_i[host_sel_req]; + device_wdata_o[device] = host_wdata_i[host_sel_req]; + device_be_o[device] = host_be_i[host_sel_req]; + end else begin + device_req_o[device] = 1'b0; + device_we_o[device] = 1'b0; + device_addr_o[device] = 'b0; + device_wdata_o[device] = 'b0; + device_be_o[device] = 'b0; + end + end + end + + always_comb begin + for (integer host = 0; host < NrHosts; host = host + 1) begin + if ($clog2(NrHosts)'(host) == host_sel_resp) begin + host_rvalid_o[host] = device_rvalid_i[device_sel_resp]; + host_err_o[host] = device_err_i[device_sel_resp]; + host_rdata_o[host] = device_rdata_i[device_sel_resp]; + end else begin + host_gnt_o[host] = 1'b0; + host_rvalid_o[host] = 1'b0; + host_err_o[host] = 1'b0; + host_rdata_o[host] = 'b0; + end + end + host_gnt_o[host_sel_req] = host_req_i[host_sel_req]; + end +endmodule diff --git a/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv new file mode 100644 index 00000000..d8e88a2b --- /dev/null +++ b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv @@ -0,0 +1,185 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Ibex simulation to run the RISC-V compliance test on + * + * This is a toplevel wrapper for Ibex with helpers to run the RISC-V compliance + * test. It is designed for Verilator, but should equally work for other + * simulators (if the top-level clk and rst ports are replaced with a generated + * clock). + */ +module ibex_riscv_compliance ( + input IO_CLK, + input IO_RST_N +); + + parameter bit RV32E = 0; + parameter bit RV32M = 1; + + logic clk_sys, rst_sys_n; + + assign clk_sys = IO_CLK; + assign rst_sys_n = IO_RST_N; + + typedef enum { + TestUtilHost, + CoreI, + CoreD + } bus_host_e; + + typedef enum { + Ram, + TestUtilDevice + } bus_device_e; + + localparam NrDevices = 2; + localparam NrHosts = 3; + + // host and device signals + logic host_req [NrHosts]; + logic host_gnt [NrHosts]; + logic [31:0] host_addr [NrHosts]; + logic host_we [NrHosts]; + logic [ 3:0] host_be [NrHosts]; + logic [31:0] host_wdata [NrHosts]; + logic host_rvalid [NrHosts]; + logic [31:0] host_rdata [NrHosts]; + logic host_err [NrHosts]; + + // devices (slaves) + logic device_req [NrDevices]; + logic [31:0] device_addr [NrDevices]; + logic device_we [NrDevices]; + logic [ 3:0] device_be [NrDevices]; + logic [31:0] device_wdata [NrDevices]; + logic device_rvalid [NrDevices]; + logic [31:0] device_rdata [NrDevices]; + logic device_err [NrDevices]; + + // Device address mapping + logic [31:0] cfg_device_addr_base [NrDevices]; + logic [31:0] cfg_device_addr_mask [NrDevices]; + assign cfg_device_addr_base[Ram] = 32'h0; + assign cfg_device_addr_mask[Ram] = ~32'hFFFF; // 64 kB + assign cfg_device_addr_base[TestUtilDevice] = 32'h20000; + assign cfg_device_addr_mask[TestUtilDevice] = ~32'h3FF; // 1 kB + + + bus #( + .NrDevices (NrDevices), + .NrHosts (NrHosts ), + .DataWidth (32 ), + .AddressWidth(32 ) + ) u_bus ( + .clk_i (clk_sys), + .rst_ni (rst_sys_n), + + .host_req_i (host_req ), + .host_gnt_o (host_gnt ), + .host_addr_i (host_addr ), + .host_we_i (host_we ), + .host_be_i (host_be ), + .host_wdata_i (host_wdata ), + .host_rvalid_o (host_rvalid ), + .host_rdata_o (host_rdata ), + .host_err_o (host_err ), + + .device_req_o (device_req ), + .device_addr_o (device_addr ), + .device_we_o (device_we ), + .device_be_o (device_be ), + .device_wdata_o (device_wdata ), + .device_rvalid_i (device_rvalid), + .device_rdata_i (device_rdata ), + .device_err_i (device_err ), + + .cfg_device_addr_base, + .cfg_device_addr_mask + ); + + ibex_core #( + .DmHaltAddr(32'h00000000), + .DmExceptionAddr(32'h00000000), + .RV32E(RV32E), + .RV32M(RV32M) + ) u_core ( + .clk_i (clk_sys), + .rst_ni (rst_sys_n), + + .test_en_i ('b0), + + .core_id_i (4'b0), + .cluster_id_i (6'b0), + // First instruction executed is at 0x0 + 0x80 + .boot_addr_i (32'h00000000), + + .instr_req_o (host_req[CoreI]), + .instr_gnt_i (host_gnt[CoreI]), + .instr_rvalid_i (host_rvalid[CoreI]), + .instr_addr_o (host_addr[CoreI]), + .instr_rdata_i (host_rdata[CoreI]), + + .data_req_o (host_req[CoreD]), + .data_gnt_i (host_gnt[CoreD]), + .data_rvalid_i (host_rvalid[CoreD]), + .data_we_o (host_we[CoreD]), + .data_be_o (host_be[CoreD]), + .data_addr_o (host_addr[CoreD]), + .data_wdata_o (host_wdata[CoreD]), + .data_rdata_i (host_rdata[CoreD]), + .data_err_i (host_err[CoreD]), + + .irq_software_i (1'b0), + .irq_timer_i (1'b0), + .irq_external_i (1'b0), + .irq_fast_i (15'b0), + .irq_nm_i (1'b0), + + .debug_req_i ('b0), + + .fetch_enable_i ('b1) + ); + + // SRAM block for instruction and data storage + ram_1p #( + .Depth(64*1024/4) + ) u_ram ( + .clk_i (clk_sys), + .rst_ni (rst_sys_n), + .req_i (device_req[Ram]), + .we_i (device_we[Ram]), + .be_i (device_be[Ram]), + .addr_i (device_addr[Ram]), + .wdata_i (device_wdata[Ram]), + .rvalid_o (device_rvalid[Ram]), + .rdata_o (device_rdata[Ram]) + ); + + // RISC-V test utility, used by the RISC-V compliance test to interact with + // the simulator. + riscv_testutil + u_riscv_testutil( + .clk_i (clk_sys), + .rst_ni (rst_sys_n), + + // Device port + .dev_req_i (device_req[TestUtilDevice]), + .dev_we_i (device_we[TestUtilDevice]), + .dev_addr_i (device_addr[TestUtilDevice]), + .dev_wdata_i (device_wdata[TestUtilDevice]), + .dev_rvalid_o (device_rvalid[TestUtilDevice]), + .dev_rdata_o (device_rdata[TestUtilDevice]), + .dev_be_i (device_be[TestUtilDevice]), + .dev_err_o (device_err[TestUtilDevice]), + + // Host port + .host_req_o (host_req[TestUtilHost]), + .host_gnt_i (host_gnt[TestUtilHost]), + .host_rvalid_i (host_rvalid[TestUtilHost]), + .host_addr_o (host_addr[TestUtilHost]), + .host_rdata_i (host_rdata[TestUtilHost]) + ); + +endmodule diff --git a/dv/riscv_compliance/rtl/prim_clock_gating.sv b/dv/riscv_compliance/rtl/prim_clock_gating.sv new file mode 100644 index 00000000..ed13f44c --- /dev/null +++ b/dv/riscv_compliance/rtl/prim_clock_gating.sv @@ -0,0 +1,24 @@ +// 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 + +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 diff --git a/dv/riscv_compliance/rtl/ram_1p.sv b/dv/riscv_compliance/rtl/ram_1p.sv new file mode 100644 index 00000000..04de31d2 --- /dev/null +++ b/dv/riscv_compliance/rtl/ram_1p.sv @@ -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 + +/** + * Single-port RAM with 1 cycle read/write delay, 32 bit words + */ +module ram_1p #( + parameter int Depth = 128 +) ( + input clk_i, + input rst_ni, + + input req_i, + input we_i, + input [ 3:0] be_i, + input [31:0] addr_i, + input [31:0] wdata_i, + output logic rvalid_o, + output logic [31:0] rdata_o +); + + localparam int Aw = $clog2(Depth); + + logic [31:0] mem [Depth]; + + logic [Aw-1:0] addr_idx; + assign addr_idx = addr_i[Aw-1+2:2]; + logic [31-Aw:0] unused_addr_parts; + assign unused_addr_parts = {addr_i[31:Aw+2], addr_i[1:0]}; + + always @(posedge clk_i) begin + if (req_i) begin + if (we_i) begin + for (int i = 0; i < 4; i = i + 1) begin + if (be_i[i] == 1'b1) begin + mem[addr_idx][i*8 +: 8] <= wdata_i[i*8 +: 8]; + end + end + end + rdata_o <= mem[addr_idx]; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_o <= '0; + end else begin + rvalid_o <= req_i; + end + end + + `ifdef VERILATOR + export "DPI-C" task simutil_verilator_memload; + + task simutil_verilator_memload; + input string file; + $readmemh(file, mem); + endtask + `endif + + `ifdef SRAM_INIT_FILE + localparam MEM_FILE = `"`SRAM_INIT_FILE`"; + initial begin + $display("Initializing SRAM from %s", MEM_FILE); + $readmemh(MEM_FILE, mem); + end + `endif +endmodule + diff --git a/dv/riscv_compliance/rtl/riscv_testutil.sv b/dv/riscv_compliance/rtl/riscv_testutil.sv new file mode 100644 index 00000000..961867c2 --- /dev/null +++ b/dv/riscv_compliance/rtl/riscv_testutil.sv @@ -0,0 +1,158 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/** + * RISC-V Compliance Test helper module (simulation only) + * + * This module lets the RISC-V compliance test software interact with the + * "outside world". + * + * It consists of a bus device and a bus host. + * + * Through the bus device, the software can interact with the outside world and + * configure this module. The following registers are supported: + * 0x0: read the signature and halt the execution + * 0x4: set the signature start address + * 0x8: set the signature end address + * + * When register 0x0 is written with an arbitrary value, the test signature is + * read through the bus device, and written to STDOUT, prefixed with + * "SIGNATURE: ". + */ +module riscv_testutil ( + input clk_i, + input rst_ni, + + // bus device (slave) interface + input dev_req_i, + input dev_we_i, + input [31:0] dev_addr_i, + input [31:0] dev_wdata_i, + input [ 3:0] dev_be_i, + + output logic dev_rvalid_o, + output logic [31:0] dev_rdata_o, + output logic dev_err_o, + + // bus host (master) interface + output logic host_req_o, + input host_gnt_i, + input host_rvalid_i, + output logic [31:0] host_addr_o, + input [31:0] host_rdata_i +); + // ======= Bus device for interaction with software ======= // + + localparam ADDR_HALT = 0; + localparam ADDR_SET_BEGIN_SIGNATURE = 4; + localparam ADDR_SET_END_SIGNATURE = 8; + + // 1 kB address space for this peripheral + logic [21:0] unused_addr; + assign unused_addr = dev_addr_i[31:10]; + + logic [31:0] begin_signature_addr_d, begin_signature_addr_q; + logic [31:0] end_signature_addr_d, end_signature_addr_q; + + logic read_signature_and_terminate; + always_comb begin + read_signature_and_terminate = 1'b0; + begin_signature_addr_d = begin_signature_addr_q; + end_signature_addr_d = end_signature_addr_q; + + if (dev_we_i && dev_req_i) begin + case (dev_addr_i[9:0]) + ADDR_HALT: begin + read_signature_and_terminate = 1'b1; + end + ADDR_SET_BEGIN_SIGNATURE: begin + begin_signature_addr_d = dev_wdata_i; + end + ADDR_SET_END_SIGNATURE: begin + end_signature_addr_d = dev_wdata_i; + end + endcase + end + end + + always_ff @(posedge clk_i) begin + begin_signature_addr_q <= begin_signature_addr_d; + end_signature_addr_q <= end_signature_addr_d; + end + + // all responses are in the next cycle + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + dev_rvalid_o <= 1'b0; + end else begin + dev_rvalid_o <= dev_req_i; + end + end + + // only word writes are supported + assign dev_err_o = (~dev_we_i | dev_be_i != 4'hf) & dev_req_i; + assign dev_rdata_o = 32'h0; + + + // ======= FSM: Read signature from memory and dump it to STDOUT ======= // + + typedef enum logic [1:0] { + WAIT, READ, READ_FINISH, TERMINATE + } readsig_state_e; + + readsig_state_e state_q, state_d; + + logic [31:0] read_addr_d, read_addr_q; + always_comb begin + state_d = state_q; + host_req_o = 1'b0; + + unique case (state_q) + WAIT: begin + if (read_signature_and_terminate) begin + $display("Reading signature from 0x%x to 0x%x", + begin_signature_addr_q, end_signature_addr_q); + state_d = READ; + read_addr_d = begin_signature_addr_q; + end + end + + READ: begin + host_req_o = 1'b1; + host_addr_o = read_addr_q; + if (host_gnt_i) begin + read_addr_d = read_addr_q + 4; + if (read_addr_d == end_signature_addr_q) begin + state_d = READ_FINISH; + end + end + end + + READ_FINISH: begin + if (host_rvalid_i) begin + state_d = TERMINATE; + end + end + + TERMINATE: begin + $display("Terminating simulation by software request."); + $finish; + end + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= WAIT; + read_addr_q <= 0; + end else begin + state_q <= state_d; + read_addr_q <= read_addr_d; + + if (host_rvalid_i) begin + $display("SIGNATURE: 0x%x", host_rdata_i); + end + end + end +endmodule