From 6c53ffd2a688af398500f465c45cb36c77718100 Mon Sep 17 00:00:00 2001 From: Paul Scheffler Date: Wed, 26 Feb 2025 19:50:18 +0100 Subject: [PATCH] target/verilator: First work on Verilator 5 support --- .gitignore | 12 +- Bender.lock | 8 +- Bender.yml | 7 +- cheshire.mk | 56 +++-- target/sim/src/elfloader.cpp | 32 ++- target/sim/src/jtag_test_simple.sv | 380 +++++++++++++++++++++++++++++ target/sim/src/tb_cheshire_soc.sv | 8 +- target/sim/src/vip_cheshire_soc.sv | 84 +++++-- target/sim/verilator/.gitkeep | 0 9 files changed, 511 insertions(+), 76 deletions(-) create mode 100644 target/sim/src/jtag_test_simple.sv create mode 100644 target/sim/verilator/.gitkeep diff --git a/.gitignore b/.gitignore index 4c8e9b871..b9e789f42 100644 --- a/.gitignore +++ b/.gitignore @@ -26,21 +26,22 @@ site/ *.dtb sw/deps/.patched -# Test models +# Simulation target/sim/models target/sim/dramsys +target/sim**/*.log +target/sim**/*.dasm # VSIM generated files target/sim/vsim/compile.*.tcl -target/sim/vsim/*.log target/sim/vsim/modelsim.ini target/sim/vsim/transcript target/sim/vsim/vsim.wlf +target/sim/vsim/*.vstf target/sim/vsim/work/ # VCS generated files target/sim/vcs/compile.*.sh -target/sim/vcs/*.log target/sim/vcs/AN.DB target/sim/vcs/simv* target/sim/vcs/csrc @@ -48,6 +49,11 @@ target/sim/vcs/ucli.key target/sim/vcs/work* target/sim/vcs/vc_hdrs.h +# Verilator generated files +target/sim/verilator/*.flist +target/sim/verilator/obj_dir +target/sim/verilator/V* + # Xilinx generated files target/xilinx/build target/xilinx/out diff --git a/Bender.lock b/Bender.lock index 5697c9872..172a706a7 100644 --- a/Bender.lock +++ b/Bender.lock @@ -24,8 +24,8 @@ packages: - common_verification - tech_cells_generic axi_llc: - revision: 559bcbd09a5a884dbe31e2d72fd95d024e357f39 - version: 0.2.1 + revision: 59bb8a681347e1133f11a82190fbf4bc11900d9e + version: 0.2.2 source: Git: https://github.com/pulp-platform/axi_llc.git dependencies: @@ -85,8 +85,8 @@ packages: - common_cells - register_interface common_cells: - revision: 13f28aa0021fc22c0d01a12d618fda58d2c93239 - version: 1.33.0 + revision: 9afda9abb565971649c2aa0985639c096f351171 + version: 1.38.0 source: Git: https://github.com/pulp-platform/common_cells.git dependencies: diff --git a/Bender.yml b/Bender.yml index 30817f2c4..9e4e07ba8 100644 --- a/Bender.yml +++ b/Bender.yml @@ -14,13 +14,13 @@ package: dependencies: apb_uart: { git: "https://github.com/pulp-platform/apb_uart.git", version: 0.2.1 } axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.39.6 } - axi_llc: { git: "https://github.com/pulp-platform/axi_llc.git", version: 0.2.1 } + axi_llc: { git: "https://github.com/pulp-platform/axi_llc.git", version: 0.2.2 } axi_riscv_atomics: { git: "https://github.com/pulp-platform/axi_riscv_atomics.git", version: 0.8.2 } axi_rt: { git: "https://github.com/pulp-platform/axi_rt.git", version: 0.0.0-alpha.9 } axi_vga: { git: "https://github.com/pulp-platform/axi_vga.git", version: 0.1.3 } clic: { git: "https://github.com/pulp-platform/clic.git", version: 2.0.0 } clint: { git: "https://github.com/pulp-platform/clint.git", version: 0.2.0 } - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.33.0 } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.38.0 } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.0 } cva6: { git: "https://github.com/pulp-platform/cva6.git", rev: pulp-v1.0.0 } iDMA: { git: "https://github.com/pulp-platform/iDMA.git", version: 0.6.3 } @@ -45,10 +45,11 @@ sources: - hw/cheshire_pkg.sv - hw/cheshire_soc.sv - - target: any(simulation, test) + - target: any(verilator, simulation, test) files: - target/sim/models/s25fs512s.v - target/sim/models/24FC1025.v + - target/sim/src/jtag_test_simple.sv # TODO: Upstream this - target/sim/src/vip_cheshire_soc.sv - target/sim/src/tb_cheshire_pkg.sv - target/sim/src/fixture_cheshire_soc.sv diff --git a/cheshire.mk b/cheshire.mk index f22f3632f..49c09fa0a 100644 --- a/cheshire.mk +++ b/cheshire.mk @@ -6,14 +6,16 @@ # Christopher Reinwardt # Paul Scheffler -BENDER ?= bender -VLOGAN ?= vlogan +BENDER ?= bender +VLOGAN ?= vlogan +VERILATOR ?= oseda /scratch/vlbuild/install/bin/verilator # Caution: Questasim requires this to point to the *actual* compiler install path CXX_PATH := $(shell which $(CXX)) -VLOG_ARGS ?= -suppress 2583 -suppress 13314 -timescale 1ns/1ps -VLOGAN_ARGS ?= -kdb -nc -assert svaext +v2k -timescale=1ns/1ps +VLOG_ARGS ?= -suppress 2583 -suppress 13314 -timescale 1ns/1ps +VLOGAN_ARGS ?= -kdb -nc -assert svaext +v2k -timescale=1ns/1ps +VERILATOR_ARGS ?= --binary -j 0 -Wall -Wno-fatal -Wno-BLKANDNBLK # Common Bender flags for Cheshire RTL CHS_BENDER_RTL_FLAGS ?= -t rtl -t cva6 -t cv64a6_imafdcsclic_sv39 @@ -155,25 +157,36 @@ $(CHS_ROOT)/target/sim/vcs/compile.cheshire_soc.sh: $(CHS_ROOT)/Bender.yml $(BENDER) script vcs -t sim -t test $(CHS_BENDER_RTL_FLAGS) --vlog-arg="$(VLOGAN_ARGS)" --vlogan-bin="$(VLOGAN)" > $@ chmod +x $@ -.PRECIOUS: $(CHS_ROOT)/target/sim/models -$(CHS_ROOT)/target/sim/models: - mkdir -p $@ +$(CHS_ROOT)/target/sim/verilator/cheshire_soc.flist: $(CHS_ROOT)/Bender.yml + $(BENDER) script verilator $(CHS_BENDER_RTL_FLAGS) > $@ + # TODO: Add verilator target for these upstream to avoid patch-in + echo '$(shell $(BENDER) path axi)/src/axi_sim_mem.sv' >> $@ + echo '$(shell $(BENDER) path common_verification)/src/clk_rst_gen.sv' >> $@ + echo '$(CHS_ROOT)/target/sim/src/elfloader.cpp' >> $@ + +$(CHS_ROOT)/target/sim/verilator/V%: $(CHS_ROOT)/target/sim/verilator/cheshire_soc.flist + mkdir -p $(dir $@) + cd $(dir $@) && $(VERILATOR) $(VERILATOR_ARGS) -DASSERTS_OFF -f $< -top-module $* + ln -fs $(dir $@)/obj_dir/V$* $@ # Download (partially non-free) simulation models from publically available sources; # by running these targets or targets depending on them, you accept this (see README.md). -$(CHS_ROOT)/target/sim/models/s25fs512s.v: $(CHS_ROOT)/Bender.yml | $(CHS_ROOT)/target/sim/models - wget --no-check-certificate https://freemodelfoundry.com/fmf_vlog_models/flash/s25fs512s.v -O $@ +$(CHS_ROOT)/target/sim/models/s25fs512s.v: $(CHS_ROOT)/Bender.yml + wget -x --no-check-certificate https://freemodelfoundry.com/fmf_vlog_models/flash/s25fs512s.v -O $@ touch $@ -$(CHS_ROOT)/target/sim/models/24FC1025.v: $(CHS_ROOT)/Bender.yml | $(CHS_ROOT)/target/sim/models - wget https://ww1.microchip.com/downloads/en/DeviceDoc/24xx1025_Verilog_Model.zip -o $@ - unzip -p 24xx1025_Verilog_Model.zip 24FC1025.v > $@ - rm 24xx1025_Verilog_Model.zip +$(CHS_ROOT)/target/sim/models/24FC1025.v: $(CHS_ROOT)/Bender.yml + wget -x https://ww1.microchip.com/downloads/en/DeviceDoc/24xx1025_Verilog_Model.zip -O $@.zip + unzip -p $@.zip 24FC1025.v > $@ + rm $@.zip CHS_SIM_ALL += $(CHS_ROOT)/target/sim/models/s25fs512s.v CHS_SIM_ALL += $(CHS_ROOT)/target/sim/models/24FC1025.v CHS_SIM_ALL += $(CHS_ROOT)/target/sim/vsim/compile.cheshire_soc.tcl CHS_SIM_ALL += $(CHS_ROOT)/target/sim/vcs/compile.cheshire_soc.sh +CHS_SIM_ALL += $(CHS_ROOT)/target/sim/verilator/cheshire_soc.flist + +CHS_VERILATOR_ALL += $(CHS_ROOT)/target/sim/verilator/Vtb_cheshire_soc ########### # DRAMSys # @@ -196,14 +209,15 @@ include $(CHS_ROOT)/target/xilinx/xilinx.mk CHS_ALL += $(CHS_SW_ALL) $(CHS_HW_ALL) $(CHS_SIM_ALL) -chs-all: $(CHS_ALL) -chs-sw-all: $(CHS_SW_ALL) -chs-hw-all: $(CHS_HW_ALL) -chs-bootrom-all: $(CHS_BOOTROM_ALL) -chs-sim-all: $(CHS_SIM_ALL) -chs-dramsys-all: $(CHS_DRAMSYS_ALL) -chs-xilinx-all: $(CHS_XILINX_ALL) +chs-all: $(CHS_ALL) +chs-sw-all: $(CHS_SW_ALL) +chs-hw-all: $(CHS_HW_ALL) +chs-bootrom-all: $(CHS_BOOTROM_ALL) +chs-sim-all: $(CHS_SIM_ALL) +chs-verilator-all: $(CHS_VERILATOR_ALL) +chs-dramsys-all: $(CHS_DRAMSYS_ALL) +chs-xilinx-all: $(CHS_XILINX_ALL) -CHS_PHONY += chs-all chs-sw-all chs-hw-all chs-bootrom-all chs-sim-all chs-dramsys-all chs-xilinx-all +CHS_PHONY += chs-all chs-sw-all chs-hw-all chs-bootrom-all chs-sim-all chs-verilator-all chs-dramsys-all chs-xilinx-all .PHONY: $(CHS_PHONY) diff --git a/target/sim/src/elfloader.cpp b/target/sim/src/elfloader.cpp index 4bb0fd62d..f59de548e 100644 --- a/target/sim/src/elfloader.cpp +++ b/target/sim/src/elfloader.cpp @@ -150,7 +150,7 @@ int section_index = 0; extern "C" { char get_entry(long long *entry_ret); char get_section(long long *address_ret, long long *len_ret); - char read_section(long long address, const svOpenArrayHandle buffer, long long len); + char read_section_chunk(long long base, long long offset, char* buffer, long long len); char read_elf(const char *filename); } @@ -187,28 +187,26 @@ extern "C" char get_section(long long *address_ret, long long *len_ret) } } -extern "C" char read_section(long long address, const svOpenArrayHandle buffer, long long len) +extern "C" char read_section_chunk(long long base, long long offset, char* buffer, long long len) { - // get actual pointer - char *buf = (char *) svGetArrayPtr(buffer); - - // check that the address points to a section - if (!mems.count(address)) { - printf("[ELF] ERROR: No section found for address %p\n", address); + // check that the base address points to a section + if (!mems.count(base)) { + printf("[ELF] ERROR: No section found for base address %p\n", base); return -1; } - - // copy array - long long int len_tmp = len; - for (auto &datum : mems.find(address)->second) { - if(len_tmp-- == 0){ - printf("[ELF] ERROR: Copied 0x%lx bytes. Buffer is full but there is still data available.\n", len); - return -1; - } - *buf++ = datum; + // get memory vector for this section + auto mem = mems.find(base)->second; + + // check for out-of-bounds access + if (offset < 0 || len < 0 || offset + len > mem.size()) { + printf("[ELF] ERROR: Offset %0p, length %d out of bounds for section at %p with length %d\n", offset, len, base, mem.size()); + return -1; } + // copy data to SV array + std::copy(mem.begin() + offset, mem.begin() + offset + len, buffer); + return 0; } diff --git a/target/sim/src/jtag_test_simple.sv b/target/sim/src/jtag_test_simple.sv new file mode 100644 index 000000000..fa2f97a5a --- /dev/null +++ b/target/sim/src/jtag_test_simple.sv @@ -0,0 +1,380 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Author: Andreas Traber +// Author: Florian Zaruba +// Author: Fabian Schuiki +// Author: Thomas Benz + + +module jtag_driver_simple #( + parameter int IrLength = 0, + parameter IDCODE = 'h1, + parameter time TA = 0ns, // stimuli application time + parameter time TT = 0ns // stimuli test time +) ( + input logic jtag_tck_i, + output logic jtag_trst_no, + output logic jtag_tms_o, + output logic jtag_tdi_o, + input logic jtag_tdo_i +); + + localparam DMIWidth = $bits(dm::dmi_req_t); + + // last IR register select + logic [IrLength-1:0] ir_select = 'h1; + + task reset_master; + #TA; + jtag_tms_o = 1; + jtag_tdi_o = 0; + jtag_trst_no = 0; + repeat (2) clock(); + #TA; + jtag_trst_no = 1; + ir_select = 'h1; + clock(); + endtask + + task soft_reset(); + #TA; + jtag_tms_o = 1; + jtag_tdi_o = 0; + repeat (6) clock(); + #TA; + jtag_tms_o = 0; + clock(); + // After softreset the IR should be reset to IDCODE so we have to mirror + // this in our internal state. + ir_select = 'h1; + endtask + + // Set IR, but only if it needs to be set. + task set_ir(input logic [IrLength-1:0] opcode); + logic opcode_unpacked [IrLength]; + // check whether IR is already set to the right value + if (ir_select == opcode) return; + // {<<{opcode_unpacked}} = opcode; + for (int i = 0; i < IrLength; i++) begin + opcode_unpacked[i] = opcode[i]; + end + write_tms(1); // select DR scan + write_tms(1); // select IR scan + write_tms(0); // capture IR + write_tms(0); // shift IR + write_bits_opcode(opcode_unpacked, 1); + write_tms(1); // update IR + write_tms(0); // run test idle + ir_select = opcode; + endtask + // Go from `run_test_idle` to `shift_dr` + task shift_dr(); + write_tms(1); // select DR scan + write_tms(0); // capture DR + write_tms(0); // shift DR + endtask + + // Go to `run_test_idle` + task update_dr(bit exit_1_dr); + // depending on the state `exit_1_dr` is already reached when shifting data (`tms_on_last`). + if (exit_1_dr) write_tms(1); // exi 1 DR + write_tms(1); // update DR + write_tms(0); // run test idle + endtask + + task write_bits_opcode(input logic wdata [IrLength], input logic tms_last); + for (int i = 0; i < IrLength; i++) begin + #TA; + jtag_tdi_o = wdata[i]; + if (i == (IrLength - 1)) jtag_tms_o = tms_last; + clock(); + end + endtask + + task write_bits_32(input logic wdata [32], input logic tms_last); + for (int i = 0; i < 32; i++) begin + #TA; + jtag_tdi_o = wdata[i]; + if (i == 31) jtag_tms_o = tms_last; + clock(); + end + endtask + + task write_bits_dmi(input logic wdata [DMIWidth], input logic tms_last); + for (int i = 0; i < DMIWidth; i++) begin + #TA; + jtag_tdi_o = wdata[i]; + if (i == (DMIWidth - 1)) jtag_tms_o = tms_last; + clock(); + end + endtask + + // Assumes JTAG FSM is already in shift DR state + task readwrite_bits_32(output logic rdata [32], input logic wdata [32], input logic tms_last); + for (int i = 0; i < 32; i++) begin + #TA; + jtag_tdi_o = wdata[i]; + if (i == 31) jtag_tms_o = tms_last; // tms_last ? exit1 DR : shift DR + cycle_start(); + rdata[i] = jtag_tdo_i; + cycle_end(); + end + endtask + + // Assumes JTAG FSM is already in shift DR state + task readwrite_bits_dmi(output logic rdata [DMIWidth], input logic wdata [DMIWidth], input logic tms_last); + for (int i = 0; i < DMIWidth; i++) begin + #TA; + jtag_tdi_o = wdata[i]; + if (i == (DMIWidth - 1)) jtag_tms_o = tms_last; // tms_last ? exit1 DR : shift DR + cycle_start(); + rdata[i] = jtag_tdo_i; + cycle_end(); + end + endtask + + task wait_idle(int cycles); + repeat(cycles) clock(); + endtask + + task write_tms(input logic tms_val); + #TA; + jtag_tms_o = tms_val; + cycle_end(); + endtask + + task clock(); + cycle_start(); cycle_end(); + endtask + + task cycle_start; + #(TT - TA); + endtask + + task cycle_end; + @(posedge jtag_tck_i); + endtask +endmodule + + +// abstracts the debug module +module riscv_dbg_simple #( + parameter int IrLength = 5, + parameter IDCODE = 'h1, + parameter DTMCSR = 'h10, + parameter DMIACCESS = 'h11, + parameter time TA = 0ns, // stimuli application time + parameter time TT = 0ns // stimuli test time +) ( + input logic jtag_tck_i, + output logic jtag_trst_no, + output logic jtag_tms_o, + output logic jtag_tdi_o, + input logic jtag_tdo_i +); + + localparam DMIWidth = $bits(dm::dmi_req_t); + + jtag_driver_simple #( + .IrLength ( IrLength ), + .TA ( TA ), + .TT ( TT ) + ) jtag ( + .jtag_tck_i, + .jtag_trst_no, + .jtag_tms_o, + .jtag_tdi_o, + .jtag_tdo_i + ); + + task reset_master(); + jtag.reset_master(); + jtag.soft_reset(); + endtask + + task wait_idle(int cycles); + jtag.wait_idle(cycles); + endtask + + task get_idcode(output logic [31:0] idcode); + logic read_data [32], write_data [32]; + write_data = '{default: 1'b0}; + jtag.set_ir(IDCODE); + jtag.shift_dr(); + jtag.readwrite_bits_32(read_data, write_data, 1'b0); + jtag.update_dr(1'b1); + // idcode = {<<{read_data}}; + for (int i = 0; i < 32; i++) begin + idcode[i] = read_data[i]; + end + endtask + + task write_dtmcs(input logic [31:0] data); + logic write_data [32]; + logic [31:0] write_data_packed; + write_data_packed = {data}; + // {<<{write_data}} = write_data_packed; + for (int i = 0; i < 32; i++) begin + write_data[i] = write_data_packed[i]; + end + jtag.set_ir(DTMCSR); + jtag.shift_dr(); + jtag.write_bits_32(write_data, 1'b1); + jtag.update_dr(1'b0); + endtask + + task read_dtmcs(output dm::dtmcs_t data, input int wait_cycles = 10); + logic read_data [32], write_data [32]; + jtag.set_ir(DTMCSR); + jtag.shift_dr(); + // shift out read data + // {<<{write_data}} = 32'b0; + for (int i = 0; i < 32; i++) begin + write_data[i] = '0; + end + jtag.readwrite_bits_32(read_data, write_data, 1'b1); + jtag.update_dr(1'b0); + // data = dm::dtmcs_t'({<<{read_data}}); + for (int i = 0; i < 32; i++) begin + data[i] = dm::dtmcs_t'(read_data[i]); + end + endtask + + task reset_dmi(); + logic [31:0] dmireset; + dmireset = 1 << 16; + write_dtmcs(dmireset); + endtask + + task write_dmi(input dm::dm_csr_e address, input logic [31:0] data); + logic write_data [DMIWidth]; + logic [DMIWidth-1:0] write_data_packed; + write_data_packed = {address, data, dm::DTM_WRITE}; + // {<<{write_data}} = write_data_packed; + for (int i = 0; i < DMIWidth; i++) begin + write_data[i] = write_data_packed[i]; + end + jtag.set_ir(DMIACCESS); + jtag.shift_dr(); + jtag.write_bits_dmi(write_data, 1'b1); + jtag.update_dr(1'b0); + endtask + + task read_dmi(input dm::dm_csr_e address, output logic [31:0] data, input int wait_cycles = 10, + output dm::dtm_op_status_e op); + logic read_data [DMIWidth], write_data [DMIWidth]; + automatic logic [DMIWidth-1:0] data_out = 0; + automatic logic [DMIWidth-1:0] write_data_packed = {address, 32'b0, dm::DTM_READ}; + // {<<{write_data}} = write_data_packed; + for (int i = 0; i < DMIWidth; i++) begin + write_data[i] = write_data_packed[i]; + end + jtag.set_ir(DMIACCESS); + // send read command + jtag.shift_dr(); + jtag.write_bits_dmi(write_data, 1'b1); + jtag.update_dr(1'b0); + jtag.wait_idle(wait_cycles); + // shift out read data + jtag.shift_dr(); + write_data_packed = {address, 32'b0, dm::DTM_NOP}; + // {<<{write_data}} = write_data_packed; + for (int i = 0; i < DMIWidth; i++) begin + write_data[i] = write_data_packed[i]; + end + jtag.readwrite_bits_dmi(read_data, write_data, 1'b1); + jtag.update_dr(1'b0); + // data_out = {<<{read_data}}; + for (int i = 0; i < DMIWidth; i++) begin + data_out[i] = read_data[i]; + end + op = dm::dtm_op_status_e'(data_out[1:0]); + data = data_out[33:2]; + endtask + + // Repeatedly read DMI until we get a valid response. + // The delay between Update-DR and Capture-DR of + // successive operations is automatically adjusted through + // an exponential backoff scheme. + // Note: read operations which have side-effects (e.g. + // reading SBData0) should not use this function + task read_dmi_exp_backoff(input dm::dm_csr_e address, output logic [31:0] data); + logic read_data [DMIWidth], write_data [DMIWidth]; + logic [DMIWidth-1:0] write_data_packed; + automatic logic [DMIWidth-1:0] data_out = 0; + automatic dm::dtm_op_status_e op = dm::DTM_SUCCESS; + automatic int trial_idx = 0; + automatic int wait_cycles = 8; + + do begin + if (trial_idx != 0) begin + // Not entered upon first iteration, resets the + // sticky error state if previous read was unsuccessful + reset_dmi(); + end + read_dmi(address, data, wait_cycles, op); + wait_cycles *= 2; + trial_idx++; + end while (op == dm::DTM_BUSY); + endtask + + task sba_read_double(input logic [31:0] address, output logic [63:0] data); + // Attempt the access sequence. Two timing violations may + // occur: + // 1) an operation is attempted while a DMI request is still + // in progress; + // 2) a SB read is attempted while a read is still in progress + // or a SB access is attempted while one is in progress + // In either case the whole sequence must be re-attempted with + // increased delays. + // Case 1) is intercepted when the op returned by a read is == DTM_BUSY, + // the sequence can be interrupted early and the delay to be adjusted is + // that between the update phase and the capture phase of a successive op. + // Case 2) is intercepted at the end of the sequence by reading the + // SBCS register, and checking sbbusyerror. In this case the delay to be + // adjusted is that before the SBData read operations. + dm::dtm_op_status_e op; + automatic int dmi_wait_cycles = 2; + automatic int sba_wait_cycles = 2; + automatic dm::sbcs_t sbcs = '{sbreadonaddr: 1, sbaccess: 3, default: '0}; + dm::sbcs_t read_sbcs; + // Check address is 64b aligned + assert (address[2:0] == '0) else $error("[JTAG] 64b-unaligned accesses not supported"); + // Start SBA sequence attempts + while (1) begin + automatic bit failed = 0; + write_dmi(dm::SBCS, sbcs); + write_dmi(dm::SBAddress0, address); + wait_idle(sba_wait_cycles); + read_dmi(dm::SBData1, data[63:32], dmi_wait_cycles, op); + // Skip second read if we already have a DTM busy error + // else we can override op + if (op != dm::DTM_BUSY) begin + read_dmi(dm::SBData0, data[31:0], dmi_wait_cycles, op); + end + // If we had a DTM_BUSY error, increase dmi_wait_cycles and clear error + if (op == dm::DTM_BUSY) begin + dmi_wait_cycles *= 2; + failed = 1'b1; + reset_dmi(); + end + // Test sbbusyerror and wait for sbbusy == 0 + // Error is cleared in next iteration when writing SBCS + do begin + sbcs.sbbusyerror = 1'b0; + read_dmi_exp_backoff(dm::SBCS, read_sbcs); + if (read_sbcs.sbbusyerror) begin + sbcs.sbbusyerror = 1'b1; // set 1 to clear + sba_wait_cycles *= 2; + failed = 1'b1; + end + if (read_sbcs.sbbusy) wait_idle(sba_wait_cycles); + end while (read_sbcs.sbbusy); + // Exit loop if sequence was successful + if (!failed) break; + end + endtask + +endmodule \ No newline at end of file diff --git a/target/sim/src/tb_cheshire_soc.sv b/target/sim/src/tb_cheshire_soc.sv index 57a15e8b6..4cbc7f0b3 100644 --- a/target/sim/src/tb_cheshire_soc.sv +++ b/target/sim/src/tb_cheshire_soc.sv @@ -30,21 +30,26 @@ module tb_cheshire_soc #( if (!$value$plusargs("IMAGE=%s", boot_hex)) boot_hex = ""; // Set boot mode and preload boot image if there is one +`ifndef VERILATOR fix.vip.set_boot_mode(boot_mode); fix.vip.i2c_eeprom_preload(boot_hex); fix.vip.spih_norflash_preload(boot_hex); +`endif // Wait for reset fix.vip.wait_for_reset(); +`ifndef VERILATOR // Preload in idle mode or wait for completion in autonomous boot if (boot_mode == 0) begin // Idle boot: preload with the specified mode case (preload_mode) 0: begin // JTAG +`endif fix.vip.jtag_init(); fix.vip.jtag_elf_run(preload_elf); fix.vip.jtag_wait_for_eoc(exit_code); +`ifndef VERILATOR end 1: begin // Serial Link fix.vip.slink_elf_run(preload_elf); fix.vip.slink_wait_for_eoc(exit_code); @@ -61,9 +66,10 @@ module tb_cheshire_soc #( fix.vip.jtag_init(); fix.vip.jtag_wait_for_eoc(exit_code); end +`endif // Wait for the UART to finish reading the current byte - wait (fix.vip.uart_reading_byte == 0); + fix.vip.uart_flush(); $finish; end diff --git a/target/sim/src/vip_cheshire_soc.sv b/target/sim/src/vip_cheshire_soc.sv index 57b143d40..e8dab5ace 100644 --- a/target/sim/src/vip_cheshire_soc.sv +++ b/target/sim/src/vip_cheshire_soc.sv @@ -84,15 +84,33 @@ module vip_cheshire_soc import cheshire_pkg::*; #( // DPI // /////////// + // Chunk section reads as Verilator does not support unbound arrays as DPI arguments. + localparam int unsigned ElfReadChunk = 4096; + import "DPI-C" function byte read_elf(input string filename); import "DPI-C" function byte get_entry(output longint entry); import "DPI-C" function byte get_section(output longint address, output longint len); - import "DPI-C" context function byte read_section(input longint address, inout byte buffer[], input longint len); + import "DPI-C" function byte read_section_chunk(input longint address, + input longint offset, inout byte buffer [ElfReadChunk], input longint len); + + // Wrapper task for full section reads + function automatic read_section(input longint address, ref byte buffer[], input longint len); + for (longint i = 0; i < len ; i += ElfReadChunk) begin + byte chunk [ElfReadChunk]; + int unsigned chunk_len = (ElfReadChunk < len - i) ? ElfReadChunk : (len - i); + byte ret = read_section_chunk(address, i, chunk, chunk_len); + if (ret) return ret; + // TODO: is there really no faster way to copy data? + for (longint k = 0; k < chunk_len; k++) buffer[i+k] = chunk[k]; + end + endfunction //////////// // DRAM // //////////// +`ifndef VERILATOR + if (UseDramSys) begin : gen_dramsys dram_sim_engine #( .ClkPeriod ( ClkPeriodSys ) @@ -155,6 +173,8 @@ module vip_cheshire_soc import cheshire_pkg::*; #( ); end +`endif + /////////////////////////////// // SoC Clock, Reset, Modes // /////////////////////////////// @@ -209,26 +229,20 @@ module vip_cheshire_soc import cheshire_pkg::*; #( .rst_no ( ) ); - // Define test bus and driver - JTAG_DV jtag(jtag_tck); - - typedef jtag_test::riscv_dbg #( + riscv_dbg_simple #( .IrLength ( 5 ), .TA ( ClkPeriodJtag * TAppl ), .TT ( ClkPeriodJtag * TTest ) - ) riscv_dbg_t; - - riscv_dbg_t::jtag_driver_t jtag_dv = new (jtag); - riscv_dbg_t jtag_dbg = new (jtag_dv); - - // Connect DUT to test bus - assign jtag_trst_n = jtag.trst_n; - assign jtag_tms = jtag.tms; - assign jtag_tdi = jtag.tdi; - assign jtag.tdo = jtag_tdo; + ) jtag_dbg ( + .jtag_tck_i ( jtag_tck ), + .jtag_trst_no ( jtag_trst_n ), + .jtag_tms_o ( jtag_tms ), + .jtag_tdi_o ( jtag_tdi ), + .jtag_tdo_i ( jtag_tdo ) + ); initial begin - wait (!rst_n); + #(ClkPeriodSys/2); jtag_dbg.reset_master(); end @@ -405,6 +419,7 @@ module vip_cheshire_soc import cheshire_pkg::*; #( logic uart_boot_ena; logic uart_boot_eoc; logic uart_reading_byte; + string uart_buffer = ""; initial begin uart_rx = 1; @@ -465,11 +480,9 @@ module vip_cheshire_soc import cheshire_pkg::*; #( endtask // Continually read characters and print lines - // TODO: we should be able to support CR properly, but buffers are hard to deal with... initial begin - static byte_bt uart_read_buf [$]; byte_bt bite; - string line; + static int unsigned pos = 0; wait_for_reset(); forever begin uart_read_byte(bite); @@ -477,21 +490,32 @@ module vip_cheshire_soc import cheshire_pkg::*; #( uart_boot_byte = bite; uart_boot_ena = 0; end else if (bite == "\n") begin - if (uart_read_buf.size() > 0) begin - line = {>>8{uart_read_buf}}; - $display("[UART] %s", line); - uart_read_buf.delete(); - end else begin - $display("[UART]"); - end + $display("[UART] %s", uart_buffer); + // Respect offset of newline without CR + uart_buffer = pos ? {(pos){" "}} : ""; + end else if (bite == "\x0d") begin + pos = 0; end else if (bite == UartDebugEoc) begin uart_boot_eoc = 1; end else begin - uart_read_buf.push_back(bite); + if (pos < uart_buffer.len()) uart_buffer.putc(pos, bite); + else uart_buffer = {uart_buffer, bite}; + pos++; end end end + // Wait for pending UART receives and flush line buffer + task automatic uart_flush(); + // Wait for inflight reads + wait (fix.vip.uart_reading_byte == 0); + // Advance time to allow printing initial to handle a final newline + #(ClkPeriodSys); + // If there is any line data left, print it + if (uart_buffer.len()) + $display("[UFIN] %s", uart_buffer); + endtask + // A length of zero indcates a write (write lengths are inferred from their queue) task automatic uart_debug_rw(doub_bt addr, doub_bt len_or_w, ref byte_bt data [$]); byte_bt bite; @@ -576,6 +600,7 @@ module vip_cheshire_soc import cheshire_pkg::*; #( // I2C // /////////// +`ifndef VERILATOR // Write-protect only chip 0 bit [3:0] i2c_wp = 4'b0001; @@ -602,11 +627,13 @@ module vip_cheshire_soc import cheshire_pkg::*; #( if (image != "") $readmemh(image, gen_i2c_eeproms[0].i_i2c_eeprom.MemoryBlock); endtask +`endif //////////////// // SPI Host // //////////////// +`ifndef VERILATOR // We connect one chip at CS1, where we can boot from this flash. s25fs512s #( .UserPreload ( 0 ) @@ -628,11 +655,13 @@ module vip_cheshire_soc import cheshire_pkg::*; #( if (image != "") $readmemh(image, i_spi_norflash.Mem); endtask +`endif /////////////////// // Serial Link // /////////////////// +`ifndef VERILATOR axi_mst_req_t slink_axi_mst_req, slink_axi_slv_req; axi_mst_rsp_t slink_axi_mst_rsp, slink_axi_slv_rsp; @@ -949,6 +978,7 @@ module vip_cheshire_soc import cheshire_pkg::*; #( if (exit_code) $error("[SLINK] FAILED: return code %0d", exit_code); else $display("[SLINK] SUCCESS"); endtask +`endif endmodule diff --git a/target/sim/verilator/.gitkeep b/target/sim/verilator/.gitkeep new file mode 100644 index 000000000..e69de29bb