`timescale 1ns/1ps module asic_rocket_tb; localparam int CLOCK_HZ = 100000000; localparam real CLOCK_HALF_PERIOD_NS = 5.000000000; localparam real RF_CLOCK_HALF_PERIOD_NS = 30.547409; reg board_clock = 1'b0; reg board_reset = 1'b1; reg gps_rf_clk = 1'b0; reg [3:0] gps_rf_data = 4'h0; reg pmod_irq = 1'b0; wire core_clk = board_clock; wire core_reset; wire uart_txd; wire spi_sck; wire spi_csn; wire [3:0] spi_dq_i; wire [3:0] spi_dq_o; wire [3:0] spi_dq_oe; wire jtag_tck; wire jtag_tms; wire jtag_tdi; wire jtag_trst_n; wire simjtag_tck; wire simjtag_tms; wire simjtag_tdi; wire simjtag_trst_n; wire jtag_tdo_data; wire jtag_tdo_driven; wire debug_ndreset; wire debug_dmactive; wire [31:0] jtag_exit; wire uart_valid; wire [7:0] uart_data; reg simjtag_enable = 1'b0; reg pmod_irq_rtl_only = 1'b0; reg [7:0] last_uart_byte = 8'h00; integer app_match_count = 0; integer gps_match_count = 0; integer uart_toggle_count = 0; integer pmod_irq_rise_count = 0; reg main_return_seen = 1'b0; reg [15:0] main_return_code_q = 16'hffff; reg pmod_irq_pulse_done = 1'b0; reg pmod_irq_pending_seen = 1'b0; reg uart_txd_q = 1'b1; wire core_wfi; wire [63:0] main_return_status; wire [15:0] main_return_code; initial begin simjtag_enable = $test$plusargs("jtag_rbb_enable"); pmod_irq_rtl_only = $test$plusargs("pmod_irq_rtl_only"); end always #(CLOCK_HALF_PERIOD_NS) board_clock = ~board_clock; always #(RF_CLOCK_HALF_PERIOD_NS) gps_rf_clk = ~gps_rf_clk; always @(posedge gps_rf_clk or posedge board_reset) begin if (board_reset) begin gps_rf_data <= 4'h0; end else begin gps_rf_data <= gps_rf_data + 4'h1; end end assign jtag_tck = simjtag_enable ? simjtag_tck : 1'b1; assign jtag_tms = simjtag_enable ? simjtag_tms : 1'b1; assign jtag_tdi = simjtag_enable ? simjtag_tdi : 1'b1; assign jtag_trst_n = simjtag_enable ? simjtag_trst_n : 1'b1; initial begin repeat (32) @(posedge board_clock); board_reset = 1'b0; end // Model an asynchronous, active-high sensor interrupt. Four square-wave // periods deliberately straddle core clock edges and exercise the Rocket // interrupt synchronizer before reaching PLIC device 11. initial begin wait (core_reset === 1'b0); repeat (32) @(posedge core_clk); repeat (4) begin #(3.0) pmod_irq = 1'b1; repeat (8) @(posedge core_clk); #(2.0) pmod_irq = 1'b0; repeat (8) @(posedge core_clk); end pmod_irq_pulse_done = 1'b1; end always @(posedge pmod_irq) begin pmod_irq_rise_count = pmod_irq_rise_count + 1; end always @(posedge core_clk) begin uart_txd_q <= uart_txd; if (!core_reset && uart_txd_q !== uart_txd) begin uart_toggle_count <= uart_toggle_count + 1; end end asic_rocket_top dut ( .board_clock(board_clock), .board_reset(board_reset), .gps_rf_clk(gps_rf_clk), .gps_rf_data(gps_rf_data), .pmod_irq(pmod_irq), .uart_rxd(1'b1), .uart_txd(uart_txd), .spi_sck(spi_sck), .spi_csn(spi_csn), .spi_dq_i(spi_dq_i), .spi_dq_o(spi_dq_o), .spi_dq_oe(spi_dq_oe), .jtag_tck(jtag_tck), .jtag_tms(jtag_tms), .jtag_tdi(jtag_tdi), .jtag_trst_n(jtag_trst_n), .jtag_tdo_data(jtag_tdo_data), .jtag_tdo_driven(jtag_tdo_driven), .debug_ndreset(debug_ndreset), .debug_dmactive(debug_dmactive) ); assign core_reset = dut.core_reset; assign core_wfi = dut.u_rocket.tile_prci_domain.element_reset_domain.rockettile.core.io_wfi; assign main_return_status = dut.u_rocket.tile_prci_domain.element_reset_domain.rockettile.core.csr.reg_mscratch; assign main_return_code = main_return_status[15:0]; // pmod_irq is source 10 in the zero-based TLPLIC arrays, corresponding to // architectural PLIC device ID 11. always @(posedge core_clk) begin if (core_reset) begin pmod_irq_pending_seen <= 1'b0; end else if (dut.u_rocket.plic_domain.plic.pending_10 === 1'b1) begin if (!pmod_irq_pending_seen) begin $display("ASIC PMOD IRQ: PLIC device 11 pending observed"); end pmod_irq_pending_seen <= 1'b1; end end SimJTAG #(.TICK_DELAY(3)) sim_jtag ( .clock(core_clk), .reset(core_reset), .enable(simjtag_enable), .init_done(~core_reset), .jtag_TCK(simjtag_tck), .jtag_TMS(simjtag_tms), .jtag_TDI(simjtag_tdi), .jtag_TRSTn(simjtag_trst_n), .jtag_TDO_data(jtag_tdo_data), .jtag_TDO_driven(jtag_tdo_driven), .exit(jtag_exit) ); spi_flash_model #( .IMAGE(""), .LOAD_OFFSET(32'h06000000) ) u_flash ( .sck(spi_sck), .csn(spi_csn), .dq_o(spi_dq_o), .dq_oe(spi_dq_oe), .dq_i(spi_dq_i) ); uart_console_model #( .CLOCK_HZ(CLOCK_HZ), .BAUD(115200) ) u_uart_console ( .clk(core_clk), .reset(core_reset), .rxd(uart_txd), .valid(uart_valid), .data(uart_data) ); always @(posedge core_clk) begin if (core_reset) begin app_match_count <= 0; gps_match_count <= 0; last_uart_byte <= 8'h00; end else if (uart_valid) begin last_uart_byte <= uart_data; case (app_match_count) 0: app_match_count <= (uart_data == "R") ? 1 : 0; 1: app_match_count <= (uart_data == "o") ? 2 : ((uart_data == "R") ? 1 : 0); 2: app_match_count <= (uart_data == "c") ? 3 : ((uart_data == "R") ? 1 : 0); 3: app_match_count <= (uart_data == "k") ? 4 : ((uart_data == "R") ? 1 : 0); 4: app_match_count <= (uart_data == "e") ? 5 : ((uart_data == "R") ? 1 : 0); 5: app_match_count <= (uart_data == "t") ? 6 : ((uart_data == "R") ? 1 : 0); 6: app_match_count <= (uart_data == " ") ? 7 : ((uart_data == "R") ? 1 : 0); 7: app_match_count <= (uart_data == "C") ? 8 : ((uart_data == "R") ? 1 : 0); 8: app_match_count <= (uart_data == "h") ? 9 : ((uart_data == "R") ? 1 : 0); 9: app_match_count <= (uart_data == "i") ? 10 : ((uart_data == "R") ? 1 : 0); 10: app_match_count <= (uart_data == "p") ? 11 : ((uart_data == "R") ? 1 : 0); default: app_match_count <= app_match_count; endcase case (gps_match_count) 0: gps_match_count <= (uart_data == "G") ? 1 : 0; 1: gps_match_count <= (uart_data == "P") ? 2 : ((uart_data == "G") ? 1 : 0); 2: gps_match_count <= (uart_data == "S") ? 3 : ((uart_data == "G") ? 1 : 0); 3: gps_match_count <= (uart_data == " ") ? 4 : ((uart_data == "G") ? 1 : 0); 4: gps_match_count <= (uart_data == "S") ? 5 : ((uart_data == "G") ? 1 : 0); 5: gps_match_count <= (uart_data == "y") ? 6 : ((uart_data == "G") ? 1 : 0); 6: gps_match_count <= (uart_data == "s") ? 7 : ((uart_data == "G") ? 1 : 0); 7: gps_match_count <= (uart_data == "t") ? 8 : ((uart_data == "G") ? 1 : 0); 8: gps_match_count <= (uart_data == "e") ? 9 : ((uart_data == "G") ? 1 : 0); 9: gps_match_count <= (uart_data == "m") ? 10 : ((uart_data == "G") ? 1 : 0); default: gps_match_count <= gps_match_count; endcase end end always @(posedge core_clk) begin if (!core_reset && gps_match_count == 10 && pmod_irq_pulse_done && pmod_irq_pending_seen && pmod_irq_rise_count == 4) begin $display("ASIC PMOD IRQ PASS: rises=%0d pending_seen=%0d", pmod_irq_rise_count, pmod_irq_pending_seen); $display("ASIC sanity PASS"); $finish; end end // The RTL-only mode bypasses the intentionally slow SPI-flash boot path. // It proves the complete hardware route from the asynchronous ASIC input to // architectural PLIC device 11; sim-pmod-irq retains the CPU/ISR check. always @(posedge core_clk) begin if (!core_reset && pmod_irq_rtl_only && pmod_irq_pulse_done) begin if (pmod_irq_pending_seen && pmod_irq_rise_count == 4) begin $display("ASIC PMOD IRQ RTL PASS: rises=%0d plic_device=11 pending_seen=%0d", pmod_irq_rise_count, pmod_irq_pending_seen); $display("ASIC sanity PASS"); end else begin $display("ASIC PMOD IRQ RTL FAIL: rises=%0d plic_device=11 pending_seen=%0d", pmod_irq_rise_count, pmod_irq_pending_seen); $display("ASIC sanity FAIL"); end $finish; end end always @(posedge core_clk) begin if (core_reset) begin main_return_seen <= 1'b0; main_return_code_q <= 16'hffff; end else if (!main_return_seen && main_return_status[31:16] == 16'h5a5a) begin main_return_seen <= 1'b1; main_return_code_q <= main_return_code; $display("C main returned %0d", main_return_code); end end always @(posedge core_clk) begin if (!core_reset && main_return_seen && pmod_irq_pulse_done) begin if (main_return_code_q == 16'h0000 && pmod_irq_pending_seen && pmod_irq_rise_count == 4) begin $display("ASIC PMOD IRQ PASS: rises=%0d pending_seen=%0d", pmod_irq_rise_count, pmod_irq_pending_seen); $display("ASIC sanity PASS"); end else begin $display("ASIC PMOD IRQ FAIL: rises=%0d pending_seen=%0d", pmod_irq_rise_count, pmod_irq_pending_seen); $display("ASIC sanity FAIL"); end $finish; end end always @(posedge core_clk) begin if (jtag_exit[0]) begin $display("SimJTAG exit code: %0d", jtag_exit >> 1); $finish; end end endmodule