feat: add PSRAM support and related tests

- Updated formal.mk to include PSRAM in formal verification targets and source files.
- Modified software.mk to include psram_math.c for HAL.
- Enhanced pin_map.json to map GPIO pins for PSRAM control signals.
- Refactored ip_ribp_wrapper.sv to integrate PSRAM interface and remove unused ribp interfaces.
- Updated rtl_style_audit.json to include PSRAM related files.
- Added PSRAM testbench (psram_tb.sv) for integration testing.
- Created test_psram.py to validate PSRAM controller functionality and register offsets.
- Adjusted memory map tests to reflect new PSRAM address space and size.
This commit is contained in:
maksyuki committed 2026-08-15 19:11:22 +08:00
1 parent 7eda4e6c46
commit 24d425c3e8
36 files changed
+4685 -1041

No files matched your search

+32 -6
View File
@@ -9,6 +9,7 @@
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/i2c.h>
#include <retrosoc/hal/lcd.h>
#include <retrosoc/hal/psram.h>
#include <retrosoc/hal/spisd.h>
#include <retrosoc/hal/timer.h>
#include <retrosoc/hal/uart.h>
@@ -182,6 +183,7 @@ static int test_timer_helpers(void) {
(period.load != 71999U)) {
return 1;
}
if ((rs_timer_period_from_ms(72000000U, 1000U, &period) != RS_OK) || (period.prescale != 0U) ||
(period.load != 71999999U)) {
return 2;
@@ -195,6 +197,30 @@ static int test_timer_helpers(void) {
return 0;
}
static int test_psram_helpers(void) {
rs_psram_timing_t timing;
if ((rs_psram_timing_from_hz(72000000U, 36000000U, &timing) != RS_OK) ||
(timing.half_period_cycles != 1U) || (timing.cs_setup_cycles != 1U) ||
(timing.cs_high_cycles != 4U) || (timing.cs_hold_cycles != 3U) ||
(timing.powerup_cycles != 10800U) || (timing.cs_max_low_cycles != 576U) ||
(timing.actual_sclk_hz != 36000000U) || timing.above_84mhz) {
return 1;
}
if ((rs_psram_timing_from_hz(192000000U, 133000000U, &timing) != RS_OK) ||
(timing.half_period_cycles != 1U) || (timing.actual_sclk_hz != 96000000U) ||
!timing.above_84mhz) {
return 2;
}
if ((rs_psram_timing_from_hz(0U, 36000000U, &timing) != RS_EINVAL) ||
(rs_psram_timing_from_hz(72000000U, 0U, &timing) != RS_EINVAL) ||
(rs_psram_timing_from_hz(72000000U, 134000000U, &timing) != RS_EINVAL) ||
(rs_psram_timing_from_hz(72000000U, 36000000U, NULL) != RS_EINVAL)) {
return 3;
}
return 0;
}
static int test_uart_helpers(void) {
rs_uart_timing_t timing;
@@ -267,8 +293,8 @@ static int test_ps2_decoders(void) {
ps2_mouse_event_t mouse_event;
ps2_keyboard_decoder_init(&keyboard);
if (!ps2_keyboard_decode_byte(&keyboard, UINT8_C(0x1C), &key_event) ||
!key_event.pressed || key_event.extended || (key_event.scan_code != UINT16_C(0x001C))) {
if (!ps2_keyboard_decode_byte(&keyboard, UINT8_C(0x1C), &key_event) || !key_event.pressed ||
key_event.extended || (key_event.scan_code != UINT16_C(0x001C))) {
return 1;
}
if (ps2_keyboard_decode_byte(&keyboard, UINT8_C(0xE0), &key_event) ||
@@ -282,8 +308,8 @@ static int test_ps2_decoders(void) {
if (ps2_mouse_decode_byte(&mouse, UINT8_C(0x08), &mouse_event) ||
ps2_mouse_decode_byte(&mouse, UINT8_C(0x02), &mouse_event) ||
ps2_mouse_decode_byte(&mouse, UINT8_C(0x01), &mouse_event) ||
!ps2_mouse_decode_byte(&mouse, UINT8_C(0x3F), &mouse_event) ||
(mouse_event.dx != 2) || (mouse_event.dy != 1) || (mouse_event.wheel != -1) ||
!ps2_mouse_decode_byte(&mouse, UINT8_C(0x3F), &mouse_event) || (mouse_event.dx != 2) ||
(mouse_event.dy != 1) || (mouse_event.wheel != -1) ||
((mouse_event.buttons & UINT8_C(0x18)) != UINT8_C(0x18))) {
return 3;
}
@@ -330,8 +356,8 @@ static int test_video_parser(void) {
int main(void) {
const int results[] = {
test_string_helpers(), test_formatter(), test_compiler_helpers(), test_wait_helper(),
test_ws2812_helpers(), test_timer_helpers(), test_uart_helpers(), test_i2c_helpers(),
test_gpio_helpers(), test_ps2_decoders(), test_wav_parser(),
test_ws2812_helpers(), test_timer_helpers(), test_psram_helpers(), test_uart_helpers(),
test_i2c_helpers(), test_gpio_helpers(), test_ps2_decoders(), test_wav_parser(),
test_video_parser(),
};
+437
View File
@@ -0,0 +1,437 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`timescale 1ns / 1ps
`include "axi4_define.svh"
`include "psram_define.svh"
module psram_tb;
localparam logic [31:0] PsramBase = 32'h4000_0000;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic [31:0] reg_data;
logic [31:0] read_data;
logic [31:0] held_data;
logic [ 1:0] held_resp;
logic held_last;
ribp_if cfg_ribp ();
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) mem_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
psram_if psram ();
tri1 [3:0] psram_sio;
always #5 clk_i = ~clk_i;
for (genvar io_index = 0; io_index < 4; io_index++) begin : gen_psram_io
assign psram_sio[io_index] = psram.io_oe_o[io_index] ? psram.io_do_o[io_index] : 1'bz;
end
assign psram.io_di_i = psram_sio;
ribp_psram u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.cfg_ribp(cfg_ribp),
.mem_axi4(mem_axi4),
.psram (psram)
);
ESP_PSRAM64H #(
.ID (0),
.MEMORY_BYTES (4096),
.INITIALIZE_MEMORY(1),
.POWER_UP_CHECK (0),
.TIMING_CHECK (0)
) u_psram0 (
.sclk(psram.sck_o),
.csn (psram.nss_o[0]),
.sio (psram_sio)
);
ESP_PSRAM64H #(
.ID (1),
.MEMORY_BYTES (4096),
.INITIALIZE_MEMORY(1),
.POWER_UP_CHECK (0),
.TIMING_CHECK (0)
) u_psram1 (
.sclk(psram.sck_o),
.csn (psram.nss_o[1]),
.sio (psram_sio)
);
ESP_PSRAM64H #(
.ID (2),
.MEMORY_BYTES (4096),
.INITIALIZE_MEMORY (1),
.POWER_UP_CHECK (0),
.TIMING_CHECK (0),
.INJECT_MISSING_DEVICE(1)
) u_psram2 (
.sclk(psram.sck_o),
.csn (psram.nss_o[2]),
.sio (psram_sio)
);
ESP_PSRAM64H #(
.ID (3),
.MEMORY_BYTES (4096),
.INITIALIZE_MEMORY(1),
.POWER_UP_CHECK (0),
.TIMING_CHECK (0)
) u_psram3 (
.sclk(psram.sck_o),
.csn (psram.nss_o[3]),
.sio (psram_sio)
);
task automatic init_axi4;
begin
mem_axi4.awid = '0;
mem_axi4.awaddr = '0;
mem_axi4.awlen = '0;
mem_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.awlock = `AXI4_LOCK_NORM;
mem_axi4.awcache = `AXI4_CACHE_NO_BUF;
mem_axi4.awprot = `AXI4_PROT_DATA;
mem_axi4.awqos = `AXI4_QOS_NORMAL;
mem_axi4.awregion = `AXI4_REGION_NORMAL;
mem_axi4.awuser = '0;
mem_axi4.awvalid = 1'b0;
mem_axi4.wdata = '0;
mem_axi4.wstrb = '0;
mem_axi4.wlast = 1'b0;
mem_axi4.wuser = '0;
mem_axi4.wvalid = 1'b0;
mem_axi4.bready = 1'b0;
mem_axi4.arid = '0;
mem_axi4.araddr = '0;
mem_axi4.arlen = '0;
mem_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.arburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.arlock = `AXI4_LOCK_NORM;
mem_axi4.arcache = `AXI4_CACHE_NO_BUF;
mem_axi4.arprot = `AXI4_PROT_DATA;
mem_axi4.arqos = `AXI4_QOS_NORMAL;
mem_axi4.arregion = `AXI4_REGION_NORMAL;
mem_axi4.aruser = '0;
mem_axi4.arvalid = 1'b0;
mem_axi4.rready = 1'b0;
end
endtask
task automatic ribp_write(input logic [31:0] offset, input logic [31:0] data,
input logic [3:0] strobe, input logic expected_error);
begin
@(negedge clk_i);
cfg_ribp.addr = offset;
cfg_ribp.wdata = data;
cfg_ribp.wstrb = strobe;
cfg_ribp.valid = 1'b1;
do @(negedge clk_i); while (!cfg_ribp.ready);
if (cfg_ribp.resp_err != expected_error) begin
$fatal(1, "RIBP write response mismatch at %08x", offset);
end
cfg_ribp.valid = 1'b0;
cfg_ribp.wstrb = '0;
@(negedge clk_i);
end
endtask
task automatic ribp_read(input logic [31:0] offset, input logic expected_error,
output logic [31:0] data);
begin
@(negedge clk_i);
cfg_ribp.addr = offset;
cfg_ribp.wdata = '0;
cfg_ribp.wstrb = '0;
cfg_ribp.valid = 1'b1;
do @(negedge clk_i); while (!cfg_ribp.ready);
if (cfg_ribp.resp_err != expected_error) begin
$fatal(1, "RIBP read response mismatch at %08x", offset);
end
data = cfg_ribp.rdata;
cfg_ribp.valid = 1'b0;
@(negedge clk_i);
end
endtask
task automatic wait_for_init;
begin
reg_data = '0;
for (int poll = 0; poll < 200; poll++) begin
repeat (20) @(posedge clk_i);
ribp_read(`RIBP_PSRAM_INTR_STATE, 1'b0, reg_data);
if (reg_data[`PSRAM_INTR_INIT_DONE]) return;
end
$fatal(1, "PSRAM initialization timed out");
end
endtask
task automatic wait_for_indirect;
begin
reg_data = '0;
for (int poll = 0; poll < 100; poll++) begin
repeat (10) @(posedge clk_i);
ribp_read(`RIBP_PSRAM_INTR_STATE, 1'b0, reg_data);
if (reg_data[`PSRAM_INTR_INDIRECT_DONE]) return;
end
$fatal(1, "PSRAM indirect command timed out");
end
endtask
task automatic axi_write_single(input logic [31:0] address, input logic [2:0] size,
input logic [31:0] data, input logic [3:0] strobe,
input logic [1:0] expected_response);
begin
@(negedge clk_i);
mem_axi4.awaddr = address;
mem_axi4.awlen = 8'd0;
mem_axi4.awsize = size;
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.awvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.awready);
@(negedge clk_i);
mem_axi4.awvalid = 1'b0;
mem_axi4.wdata = data;
mem_axi4.wstrb = strobe;
mem_axi4.wlast = 1'b1;
mem_axi4.wvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.wready);
@(negedge clk_i);
mem_axi4.wvalid = 1'b0;
do @(negedge clk_i); while (!mem_axi4.bvalid);
if (mem_axi4.bresp != expected_response) begin
$fatal(1, "AXI write response mismatch at %08x", address);
end
mem_axi4.bready = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
mem_axi4.bready = 1'b0;
mem_axi4.wlast = 1'b0;
@(negedge clk_i);
end
endtask
task automatic axi_read_single(input logic [31:0] address, input logic [2:0] size,
input logic [1:0] expected_response, output logic [31:0] data);
begin
@(negedge clk_i);
mem_axi4.araddr = address;
mem_axi4.arlen = 8'd0;
mem_axi4.arsize = size;
mem_axi4.arburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.arready);
@(negedge clk_i);
mem_axi4.arvalid = 1'b0;
do @(negedge clk_i); while (!mem_axi4.rvalid);
held_data = mem_axi4.rdata;
held_resp = mem_axi4.rresp;
held_last = mem_axi4.rlast;
repeat (3) begin
@(negedge clk_i);
if (!mem_axi4.rvalid || (mem_axi4.rdata != held_data) ||
(mem_axi4.rresp != held_resp) || (mem_axi4.rlast != held_last)) begin
$fatal(1, "AXI read response changed under backpressure");
end
end
if ((held_resp != expected_response) || !held_last) begin
$fatal(1, "AXI read response mismatch at %08x", address);
end
data = held_data;
mem_axi4.rready = 1'b1;
@(negedge clk_i);
mem_axi4.rready = 1'b0;
end
endtask
task automatic axi_write_burst4(input logic [31:0] address);
begin
@(negedge clk_i);
mem_axi4.awaddr = address;
mem_axi4.awlen = 8'd3;
mem_axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.awburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.awvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.awready);
@(negedge clk_i);
mem_axi4.awvalid = 1'b0;
for (int beat = 0; beat < 4; beat++) begin
mem_axi4.wdata = 32'hA500_1000 + beat;
mem_axi4.wstrb = 4'hF;
mem_axi4.wlast = beat == 3;
mem_axi4.wvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.wready);
@(negedge clk_i);
mem_axi4.wvalid = 1'b0;
end
do @(negedge clk_i); while (!mem_axi4.bvalid);
if (mem_axi4.bresp != `AXI4_RESP_OKAY) $fatal(1, "AXI burst write failed");
mem_axi4.bready = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
mem_axi4.bready = 1'b0;
mem_axi4.wlast = 1'b0;
@(negedge clk_i);
end
endtask
task automatic axi_read_burst4(input logic [31:0] address);
begin
@(negedge clk_i);
mem_axi4.araddr = address;
mem_axi4.arlen = 8'd3;
mem_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.arburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.arready);
@(negedge clk_i);
mem_axi4.arvalid = 1'b0;
for (int beat = 0; beat < 4; beat++) begin
if (beat == 1) repeat (3) @(negedge clk_i);
do @(negedge clk_i); while (!mem_axi4.rvalid);
if ((mem_axi4.rdata != (32'hA500_1000 + beat)) ||
(mem_axi4.rresp != `AXI4_RESP_OKAY) ||
(mem_axi4.rlast != (beat == 3))) begin
$fatal(1, "AXI burst read beat %0d failed", beat);
end
mem_axi4.rready = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
mem_axi4.rready = 1'b0;
end
end
endtask
task automatic axi_illegal_read;
integer beat;
begin
@(negedge clk_i);
mem_axi4.araddr = PsramBase + 32'h400;
mem_axi4.arlen = 8'd16;
mem_axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
mem_axi4.arburst = `AXI4_BURST_TYPE_INCR;
mem_axi4.arvalid = 1'b1;
do @(posedge clk_i); while (!mem_axi4.arready);
@(negedge clk_i);
mem_axi4.arvalid = 1'b0;
beat = 0;
while (beat < 17) begin
do @(negedge clk_i); while (!mem_axi4.rvalid);
if ((mem_axi4.rresp != `AXI4_RESP_SLAVE_ERROR) || (mem_axi4.rlast != (beat == 16))) begin
$fatal(1, "illegal AXI burst response mismatch");
end
mem_axi4.rready = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
mem_axi4.rready = 1'b0;
beat = beat + 1;
end
end
endtask
initial begin
cfg_ribp.valid = 1'b0;
cfg_ribp.addr = '0;
cfg_ribp.wdata = '0;
cfg_ribp.wstrb = '0;
init_axi4();
repeat (5) @(posedge clk_i);
rst_n_i = 1'b1;
$display("PSRAM_TB_STAGE register_setup");
ribp_write(`RIBP_PSRAM_POWERUP_CYCLES, 32'd1, 4'hF, 1'b0);
ribp_write(`RIBP_PSRAM_CS_HIGH_CYCLES, 32'd2, 4'hF, 1'b0);
ribp_write(`RIBP_PSRAM_CS_HOLD_CYCLES, 32'd3, 4'hF, 1'b0);
ribp_write(`RIBP_PSRAM_CS_MAX_LOW_CYCLES, 32'd512, 4'hF, 1'b0);
ribp_write(`RIBP_PSRAM_ACCESS_TIMEOUT_CYCLES, 32'd1024, 4'hF, 1'b0);
ribp_write(`RIBP_PSRAM_PERF_CTRL, 32'd1, 4'h1, 1'b0);
ribp_write(`RIBP_PSRAM_STATUS, 32'd1, 4'h1, 1'b1);
ribp_read(32'h0000_0002, 1'b1, reg_data);
ribp_read(32'h0000_00B0, 1'b1, reg_data);
ribp_write(`RIBP_PSRAM_COMMAND, 32'h0000_0003, 4'h1, 1'b1);
$display("PSRAM_TB_STAGE initialization");
ribp_write(`RIBP_PSRAM_COMMAND, 32'h0000_0001, 4'h1, 1'b0);
wait_for_init();
ribp_read(`RIBP_PSRAM_CHIP_PRESENT, 1'b0, reg_data);
if (reg_data[3:0] != 4'b1011) $fatal(1, "unexpected present mask");
ribp_read(`RIBP_PSRAM_CHIP_READY, 1'b0, reg_data);
if (reg_data[3:0] != 4'b1011) $fatal(1, "unexpected ready mask");
ribp_read(`RIBP_PSRAM_CHIP_ERROR, 1'b0, reg_data);
if (reg_data[3:0] != 4'b0100) $fatal(1, "missing-device error not isolated");
ribp_read(`RIBP_PSRAM_CHIP0_ID_LO, 1'b0, reg_data);
if (reg_data != 32'h0026_5D0D) $fatal(1, "chip ID mismatch");
ribp_read(`RIBP_PSRAM_CHIP_MODE, 1'b0, reg_data);
if (reg_data[3:0] != 4'b1011) $fatal(1, "QPI mode mask mismatch");
$display("PSRAM_TB_STAGE basic_axi");
axi_write_single(PsramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, 32'hDEAD_BEEF, 4'hF,
`AXI4_RESP_OKAY);
axi_read_single(PsramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, `AXI4_RESP_OKAY, read_data);
if (read_data != 32'hDEAD_BEEF) $fatal(1, "word readback mismatch");
axi_write_single(PsramBase + 32'h101, `AXI4_BURST_SIZE_1BYTE, 32'h0000_AA00, 4'h2,
`AXI4_RESP_OKAY);
axi_read_single(PsramBase + 32'h100, `AXI4_BURST_SIZE_4BYTES, `AXI4_RESP_OKAY, read_data);
if (read_data != 32'hDEAD_AAEF) $fatal(1, "byte write address or lane mismatch");
axi_write_single(PsramBase + 32'h104, `AXI4_BURST_SIZE_4BYTES, 32'd0, 4'hF, `AXI4_RESP_OKAY);
axi_write_single(PsramBase + 32'h104, `AXI4_BURST_SIZE_4BYTES, 32'h4433_2211, 4'b0101,
`AXI4_RESP_OKAY);
axi_read_single(PsramBase + 32'h104, `AXI4_BURST_SIZE_4BYTES, `AXI4_RESP_OKAY, read_data);
if (read_data != 32'h0033_0011) $fatal(1, "masked write mismatch");
$display("PSRAM_TB_STAGE burst_axi");
axi_write_burst4(PsramBase + 32'h200);
axi_read_burst4(PsramBase + 32'h200);
axi_illegal_read();
axi_read_single(PsramBase + 32'h0100_0000, `AXI4_BURST_SIZE_4BYTES, `AXI4_RESP_SLAVE_ERROR,
read_data);
axi_write_single(PsramBase + 32'h0180_0300, `AXI4_BURST_SIZE_4BYTES, 32'hC001_CAFE, 4'hF,
`AXI4_RESP_OKAY);
axi_read_single(PsramBase + 32'h0180_0300, `AXI4_BURST_SIZE_4BYTES, `AXI4_RESP_OKAY, read_data);
if (read_data != 32'hC001_CAFE) $fatal(1, "healthy chip after missing device failed");
$display("PSRAM_TB_STAGE indirect");
ribp_write(`RIBP_PSRAM_INTR_STATE, 32'hF, 4'h1, 1'b0);
ribp_write(`RIBP_PSRAM_INDIRECT_CTRL, 32'h8000_0009, 4'hF, 1'b0);
wait_for_indirect();
ribp_read(`RIBP_PSRAM_CHIP_MODE, 1'b0, reg_data);
if (!reg_data[4]) $fatal(1, "wrap32 mode did not update");
$display("PSRAM_TB_STAGE timeout");
ribp_write(`RIBP_PSRAM_CS_MAX_LOW_CYCLES, 32'd20, 4'hF, 1'b0);
axi_read_single(PsramBase + 32'h0080_0800, `AXI4_BURST_SIZE_4BYTES, `AXI4_RESP_SLAVE_ERROR,
read_data);
ribp_read(`RIBP_PSRAM_CHIP_READY, 1'b0, reg_data);
if (reg_data[3:0] != 4'b1001) $fatal(1, "timeout fault was not isolated");
ribp_write(`RIBP_PSRAM_CS_MAX_LOW_CYCLES, 32'd512, 4'hF, 1'b0);
axi_read_single(PsramBase + 32'h0180_0300, `AXI4_BURST_SIZE_4BYTES, `AXI4_RESP_OKAY, read_data);
if (read_data != 32'hC001_CAFE) $fatal(1, "healthy chip failed after timeout");
$display("PSRAM_TB_STAGE counters");
ribp_read(`RIBP_PSRAM_PERF_READ_BYTES, 1'b0, reg_data);
if (reg_data == 32'd0) $fatal(1, "read performance counter did not advance");
ribp_read(`RIBP_PSRAM_PERF_WRITE_BYTES, 1'b0, reg_data);
if (reg_data == 32'd0) $fatal(1, "write performance counter did not advance");
ribp_read(`RIBP_PSRAM_PERF_COMMANDS, 1'b0, reg_data);
if (reg_data == 32'd0) $fatal(1, "command performance counter did not advance");
$display("PSRAM controller integration test passed");
$finish;
end
endmodule
+3 -3
View File
@@ -50,12 +50,12 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
header = (tmp_path / "include/retrosoc/generated/memory_map.h").read_text(encoding="utf-8")
linker = (tmp_path / "linker/memory_regions.ld").read_text(encoding="utf-8")
assert "`define SOC_ADDR_PSRAM_END 32'h407FFFFF" in rtl
assert "`define SOC_ADDR_PSRAM_END 32'h41FFFFFF" in rtl
assert "`define SOC_ADDR_XPI_END 32'h5FFFFFFF" in rtl
assert "`define SOC_ADDR_IS_FLASH(addr) ((addr) <= `SOC_ADDR_FLASH_END)" in rtl
assert "`define CPU_RESET_ADDR `SOC_CPU_RESET_ADDR" in rtl
assert "`define SOC_ADDR_IS_RESERVED(addr)" in rtl
assert "RS_SOC_PSRAM_SIZE UINT32_C(0x00800000)" in header
assert "RS_SOC_PSRAM_SIZE UINT32_C(0x02000000)" in header
assert "RS_SOC_SDRAM_SIZE UINT32_C(0x04000000)" in header
assert "RS_SOC_SPISD_SIZE UINT32_C(0x40000000)" in header
assert "#ifdef __ASSEMBLER__" in header
@@ -78,7 +78,7 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
assert "RS_SOC_SYSCTRL_PLL_STATUS_OFFSET UINT32_C(0x0000001C)" in header
assert "RS_SOC_SYSCTRL_TEST_STATUS_OFFSET UINT32_C(0x00000084)" in header
assert "RS_SOC_SYSCTRL_RTC_WAKE_STATUS_OFFSET UINT32_C(0x00000088)" in header
assert "PSRAM (wxa!ri) : ORIGIN = 0x40000000, LENGTH = 0x00800000" in linker
assert "PSRAM (wxa!ri) : ORIGIN = 0x40000000, LENGTH = 0x02000000" in linker
def test_user_ip_is_always_emitted_for_the_fixed_platform(tmp_path: Path) -> None:
+100
View File
@@ -0,0 +1,100 @@
"""Directed tests for the ESP-PSRAM64H controller and its handwritten ABI."""
from __future__ import annotations
import os
import re
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_psram_controller_data_integrity_and_fault_isolation(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
generated = tmp_path / "generated"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/address_map/generate_memory_map.py"),
"--map",
str(ROOT / "rtl/mini/address_map/memory_map.json"),
"--output-dir",
str(generated),
"--have-sram-if",
"NO",
],
check=True,
)
output = tmp_path / "psram_tb"
ccache_tmp = tmp_path / "ccache"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"psram_tb",
"-I" + str(generated / "rtl"),
"-I" + str(ROOT / "rtl/ip/memory"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl/interface"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/ribp_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_addr_gen.sv"),
str(ROOT / "rtl/ip/memory/psram_pkg.sv"),
str(ROOT / "rtl/ip/memory/psram_axi4.sv"),
str(ROOT / "rtl/ip/memory/psram_core.sv"),
str(ROOT / "rtl/ip/memory/psram_phy.sv"),
str(ROOT / "rtl/ip/memory/psram_reg.sv"),
str(ROOT / "rtl/ip/memory/ribp_psram.sv"),
str(ROOT / "rtl/mini/dv/model/ESP_PSRAM64H.sv"),
str(ROOT / "tests/rtl/psram_tb.sv"),
"-Mdir",
str(tmp_path / "obj"),
"-o",
str(output),
],
check=True,
text=True,
capture_output=True,
env={
**os.environ,
"CCACHE_DIR": str(ccache_tmp),
"CCACHE_TEMPDIR": str(ccache_tmp),
},
)
result = subprocess.run([output], check=True, text=True, capture_output=True)
assert "PSRAM controller integration test passed" in result.stdout
def test_psram_handwritten_register_offsets_match_hal() -> None:
rtl_text = (ROOT / "rtl/ip/memory/psram_define.svh").read_text(encoding="utf-8")
hal_text = (ROOT / "crt/src/hal/psram.c").read_text(encoding="utf-8")
rtl_offsets = {
name: int(value, 16)
for name, value in re.findall(
r"`define\s+RIBP_PSRAM_([A-Z0-9_]+)\s+12'h([0-9A-Fa-f]+)",
rtl_text,
)
}
hal_offsets = {
name: int(value, 16)
for name, value in re.findall(
r"#define\s+RS_PSRAM_([A-Z0-9_]+)_OFFSET\s+UINT32_C\(0x([0-9A-Fa-f]+)\)",
hal_text,
)
}
assert hal_offsets
for name, value in hal_offsets.items():
rtl_name = "CHIP0_ID_LO" if name == "CHIP_ID_BASE" else name
assert rtl_offsets[rtl_name] == value