mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: add GPIO V2 support and related tests
- Updated to include for testing. - Modified to include GPIO header and added tests for GPIO functionality. - Created a new testbench for GPIO V2, covering register and pad-control contracts. - Removed obsolete and its associated test. - Updated to include GPIO admin base routing. - Added a new Python test for GPIO V2 register and pad-control contract. - Removed as its functionality is now covered in . - Updated memory map tests to ensure GPIO admin base is correctly referenced. - Adjusted formal filelist generation to reflect changes in GPIO handling. - Enhanced SOC topology tests to include GPIO interrupt handling.
This commit is contained in:
53 files changed
+2512
-1200
No files matched your search
@@ -0,0 +1,174 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`include "gpio_define.svh"
|
||||
|
||||
module gpio_tb;
|
||||
localparam int PIN_NUM = 4;
|
||||
localparam logic [31:0] USER_BASE = 32'h1000_0000;
|
||||
localparam logic [31:0] ADMIN_BASE = 32'h1001_4000;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
ribp_if ribp ();
|
||||
gpio_if #(PIN_NUM) gpio ();
|
||||
user_gpio_if #(PIN_NUM) user_gpio ();
|
||||
logic [31:0] read_data;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
ribp_gpio #(
|
||||
.PIN_NUM (PIN_NUM),
|
||||
.USER_BASE_ADDR (USER_BASE),
|
||||
.ADMIN_BASE_ADDR(ADMIN_BASE),
|
||||
.HAS_INPUT_CMOS (1'b1),
|
||||
.HAS_PULL_UP (1'b1),
|
||||
.HAS_PULL_DOWN (1'b1)
|
||||
) dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.ribp (ribp),
|
||||
.gpio (gpio),
|
||||
.user_gpio(user_gpio)
|
||||
);
|
||||
|
||||
task automatic write_register(input logic [31:0] address, input logic [31:0] data,
|
||||
input logic expected_error);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
ribp.addr = address;
|
||||
ribp.wdata = data;
|
||||
ribp.wstrb = 4'hF;
|
||||
ribp.valid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!ribp.ready || (ribp.resp_err !== expected_error)) begin
|
||||
$fatal(1, "write response mismatch at %08x", address);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
ribp.valid = 1'b0;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic read_register(input logic [31:0] address, output logic [31:0] data,
|
||||
input logic expected_error);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
ribp.addr = address;
|
||||
ribp.wdata = '0;
|
||||
ribp.wstrb = '0;
|
||||
ribp.valid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!ribp.ready || (ribp.resp_err !== expected_error)) begin
|
||||
$fatal(1, "read response mismatch at %08x", address);
|
||||
end
|
||||
data = ribp.rdata;
|
||||
@(negedge clk_i);
|
||||
ribp.valid = 1'b0;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
ribp.valid = 1'b0;
|
||||
ribp.addr = '0;
|
||||
ribp.wdata = '0;
|
||||
ribp.wstrb = '0;
|
||||
gpio.di_i = '0;
|
||||
gpio.alt0_do_i = '0;
|
||||
gpio.alt0_oe_i = '0;
|
||||
gpio.alt1_do_i = '0;
|
||||
gpio.alt1_oe_i = '0;
|
||||
user_gpio.do_o = '0;
|
||||
user_gpio.oe_o = '0;
|
||||
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (3) @(posedge clk_i);
|
||||
|
||||
read_register(ADMIN_BASE + `RIBP_GPIO_CAPABILITY, read_data, 1'b0);
|
||||
if (read_data !== 32'h007F_4204) $fatal(1, "capability register mismatch");
|
||||
read_register(ADMIN_BASE + `RIBP_GPIO_PAD_CAPABILITY, read_data, 1'b0);
|
||||
if (read_data !== 32'h0000_0007) $fatal(1, "pad capability register mismatch");
|
||||
read_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUTPUT_ENABLE, read_data, 1'b0);
|
||||
if (read_data !== 32'd0) $fatal(1, "output enable reset value mismatch");
|
||||
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUT_SET, 32'h1, 1'b0);
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OE_SET, 32'h1, 1'b0);
|
||||
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b11) $fatal(1, "software output failed");
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUT_TOGGLE, 32'h1, 1'b0);
|
||||
if (gpio.do_o[0] !== 1'b0) $fatal(1, "atomic output toggle failed");
|
||||
read_register(ADMIN_BASE + 32'h2, read_data, 1'b1);
|
||||
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_USER_ACCESS_MASK, 32'h2, 1'b0);
|
||||
write_register(USER_BASE + `RIBP_GPIO_USER_OUT_SET, 32'h6, 1'b0);
|
||||
read_register(USER_BASE + `RIBP_GPIO_USER_DATA_OUT, read_data, 1'b0);
|
||||
if (read_data !== 32'h2) $fatal(1, "user access mask did not isolate output data");
|
||||
|
||||
user_gpio.do_o[2] = 1'b1;
|
||||
user_gpio.oe_o[2] = 1'b1;
|
||||
@(negedge clk_i);
|
||||
ribp.addr = ADMIN_BASE + `RIBP_GPIO_ADMIN_USER_SELECT;
|
||||
ribp.wdata = 32'h4;
|
||||
ribp.wstrb = 4'hF;
|
||||
ribp.valid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (gpio.oe_o[2] !== 1'b0) $fatal(1, "user handoff did not force high impedance");
|
||||
@(negedge clk_i);
|
||||
ribp.valid = 1'b0;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if ({gpio.oe_o[2], gpio.do_o[2]} !== 2'b11) $fatal(1, "user output was not selected");
|
||||
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OE_SET, 32'h2, 1'b0);
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OPEN_DRAIN, 32'h2, 1'b0);
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUT_SET, 32'h2, 1'b0);
|
||||
if ({gpio.oe_o[1], gpio.do_o[1]} !== 2'b00) $fatal(1, "open-drain release failed");
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OUT_CLEAR, 32'h2, 1'b0);
|
||||
if ({gpio.oe_o[1], gpio.do_o[1]} !== 2'b10) $fatal(1, "open-drain low drive failed");
|
||||
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_PULL_UP, 32'h8, 1'b0);
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_PULL_DOWN, 32'h8, 1'b1);
|
||||
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_RISE_ENABLE, 32'h1, 1'b0);
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_ENABLE, 32'h1, 1'b0);
|
||||
gpio.di_i[0] = 1'b1;
|
||||
repeat (5) @(posedge clk_i);
|
||||
read_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_STATE, read_data, 1'b0);
|
||||
if ((read_data & 32'h1) == 0) $fatal(1, "rising edge interrupt was not latched");
|
||||
if (!gpio.irq_o) $fatal(1, "combined GPIO interrupt was not asserted");
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_STATE, 32'h1, 1'b0);
|
||||
if (gpio.irq_o) $fatal(1, "W1C did not clear the interrupt");
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_TEST, 32'h1, 1'b0);
|
||||
if (!gpio.irq_o) $fatal(1, "software interrupt test failed");
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_STATE, 32'h1, 1'b0);
|
||||
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_RISE_ENABLE, 32'h9, 1'b0);
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_INTR_HIGH_ENABLE, 32'h8, 1'b1);
|
||||
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_FILTER_DIV, 32'h0, 1'b0);
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_FILTER_COUNT, 32'h4, 1'b0);
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_FILTER_ENABLE, 32'h8, 1'b0);
|
||||
gpio.di_i[3] = 1'b1;
|
||||
@(posedge clk_i);
|
||||
gpio.di_i[3] = 1'b0;
|
||||
repeat (5) @(posedge clk_i);
|
||||
read_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_DATA_IN, read_data, 1'b0);
|
||||
if ((read_data & 32'h8) != 0) $fatal(1, "input filter accepted a short pulse");
|
||||
gpio.di_i[3] = 1'b1;
|
||||
repeat (8) @(posedge clk_i);
|
||||
read_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_DATA_IN, read_data, 1'b0);
|
||||
if ((read_data & 32'h8) == 0) $fatal(1, "input filter rejected a stable input");
|
||||
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_CONFIG_LOCK, 32'h1, 1'b0);
|
||||
write_register(ADMIN_BASE + `RIBP_GPIO_ADMIN_OE_CLEAR, 32'h1, 1'b1);
|
||||
if (gpio.oe_o[0] !== 1'b1) $fatal(1, "configuration lock did not protect OE");
|
||||
|
||||
$display("GPIO V2 directed test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -1,127 +0,0 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module gpio_user_mux_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
ribp_if rib ();
|
||||
gpio_if gpio ();
|
||||
user_gpio_if user_gpio ();
|
||||
logic [31:0] read_data;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
ribp_gpio dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.ribp (rib),
|
||||
.gpio (gpio),
|
||||
.user_gpio(user_gpio)
|
||||
);
|
||||
|
||||
task automatic write_register(input logic [7:0] address, input logic [31:0] data);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
rib.addr = {24'd0, address};
|
||||
rib.wdata = data;
|
||||
rib.wstrb = 4'hF;
|
||||
rib.valid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
@(negedge clk_i);
|
||||
rib.valid = 1'b0;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic read_register(input logic [7:0] address, output logic [31:0] data);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
rib.addr = {24'd0, address};
|
||||
rib.wdata = '0;
|
||||
rib.wstrb = '0;
|
||||
rib.valid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
data = rib.rdata;
|
||||
@(negedge clk_i);
|
||||
rib.valid = 1'b0;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic select_user_gpio(input logic [31:0] selection);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
rib.addr = 32'h0000_0030;
|
||||
rib.wdata = selection;
|
||||
rib.wstrb = 4'hF;
|
||||
rib.valid = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (gpio.oe_o[0] !== 1'b0) $fatal(1, "GPIO handoff did not drive high impedance");
|
||||
@(negedge clk_i);
|
||||
rib.valid = 1'b0;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
rib.valid = 1'b0;
|
||||
rib.addr = '0;
|
||||
rib.wdata = '0;
|
||||
rib.wstrb = '0;
|
||||
gpio.di_i = '0;
|
||||
gpio.alt0_do_i = '0;
|
||||
gpio.alt0_oe_i = '0;
|
||||
gpio.alt1_do_i = '0;
|
||||
gpio.alt1_oe_i = '0;
|
||||
user_gpio.do_o = '0;
|
||||
user_gpio.oe_o = '0;
|
||||
|
||||
repeat (2) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (2) @(posedge clk_i);
|
||||
|
||||
write_register(8'h00, 32'h0000_0001);
|
||||
write_register(8'h10, 32'h0000_0001);
|
||||
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b11) $fatal(1, "rib software GPIO is not active");
|
||||
|
||||
user_gpio.do_o[0] = 1'b0;
|
||||
user_gpio.oe_o[0] = 1'b1;
|
||||
select_user_gpio(32'h0000_0001);
|
||||
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b10) $fatal(1, "user GPIO was not selected");
|
||||
read_register(8'h30, read_data);
|
||||
if (read_data !== 32'h0000_0001) $fatal(1, "USER_SEL readback is incorrect");
|
||||
read_register(8'h38, read_data);
|
||||
if (read_data !== 32'h0000_0001) $fatal(1, "USER_STATUS is not active after handoff");
|
||||
|
||||
gpio.di_i[0] = 1'b1;
|
||||
#1;
|
||||
if (user_gpio.di_i[0] !== 1'b1) $fatal(1, "user GPIO input path is disconnected");
|
||||
|
||||
write_register(8'h34, 32'h0000_0001);
|
||||
read_register(8'h34, read_data);
|
||||
if (read_data !== 32'h0000_0001) $fatal(1, "USER_LOCK is not write-one-set");
|
||||
write_register(8'h30, '0);
|
||||
if ({gpio.oe_o[0], gpio.do_o[0]} !== 2'b10) $fatal(1, "USER_LOCK did not preserve ownership");
|
||||
read_register(8'h30, read_data);
|
||||
if (read_data !== 32'h0000_0001) $fatal(1, "locked USER_SEL changed");
|
||||
|
||||
gpio.alt0_do_i[1] = 1'b1;
|
||||
gpio.alt0_oe_i[1] = 1'b1;
|
||||
write_register(8'h28, 32'h0000_0002);
|
||||
if ({gpio.oe_o[1], gpio.do_o[1]} !== 2'b11) $fatal(1, "alternate GPIO mode regressed");
|
||||
|
||||
write_register(8'h04, 32'h0000_0001);
|
||||
write_register(8'h08, 32'h0000_0001);
|
||||
write_register(8'h0C, 32'h0000_0001);
|
||||
if ({gpio.cs_o[0], gpio.pu_o[0], gpio.pd_o[0]} !== 3'b111)
|
||||
$fatal(1, "rib GPIO electrical controls changed under user ownership");
|
||||
|
||||
$display("GPIO user ownership test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -140,6 +140,7 @@ module ribp_topology_tb;
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
expect_route(`SOC_ADDR_RIBP_GPIO_BASE, 1, 32'h1111_1111);
|
||||
expect_route(`SOC_ADDR_RIBP_GPIO_ADMIN_BASE, 1, 32'h1111_1111);
|
||||
expect_route(`SOC_ADDR_PSRAM_BASE, 4, 32'h4444_4444);
|
||||
expect_route(`SOC_ADDR_FLASH_BASE, 9, 32'h9999_9999);
|
||||
expect_route(`SOC_ADDR_RIBP_I2C1_BASE, 17, 32'h1234_5678);
|
||||
|
||||
Reference in new issue
Block a user