mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
merged changes in axi
This commit is contained in:
commit
cee999a201
@ -268,6 +268,27 @@ Wrappers can generated with `axil_interconnect_wrap.py`.
|
||||
|
||||
AXI lite RAM with parametrizable data and address interface widths.
|
||||
|
||||
### `axil_reg_if` module
|
||||
|
||||
AXI lite register interface with parametrizable data and address interface
|
||||
widths. Can be used to assemble a set of control registers across multiple
|
||||
modules and hierarchy levels without complicated arbitration logic. Wrapper
|
||||
for `axil_reg_if_rd` and `axil_reg_if_wr`.
|
||||
|
||||
### `axil_reg_if_rd` module
|
||||
|
||||
AXI lite register interface with parametrizable data and address interface
|
||||
widths. Read direction only. Can be used to assemble a set of control
|
||||
registers across multiple modules and hierarchy levels without complicated
|
||||
arbitration logic.
|
||||
|
||||
### `axil_reg_if_wr` module
|
||||
|
||||
AXI lite register interface with parametrizable data and address interface
|
||||
widths. Write direction only. Can be used to assemble a set of control
|
||||
registers across multiple modules and hierarchy levels without complicated
|
||||
arbitration logic.
|
||||
|
||||
### `axil_register` module
|
||||
|
||||
AXI lite register with parametrizable data and address interface widths.
|
||||
@ -393,6 +414,9 @@ registers can be individually bypassed.
|
||||
rtl/axil_crossbar_wr.v : AXI lite crossbar interconnect (write)
|
||||
rtl/axil_interconnect.v : AXI lite shared interconnect
|
||||
rtl/axil_ram.v : AXI lite RAM
|
||||
rtl/axil_reg_if.v : AXI lite register interface
|
||||
rtl/axil_reg_if_rd.v : AXI lite register interface (read)
|
||||
rtl/axil_reg_if_wr.v : AXI lite register interface (write)
|
||||
rtl/axil_register.v : AXI lite register
|
||||
rtl/axil_register_rd.v : AXI lite register (read)
|
||||
rtl/axil_register_wr.v : AXI lite register (write)
|
||||
|
@ -118,14 +118,22 @@ parameter CL_S_ACCEPT = $clog2(S_ACCEPT);
|
||||
function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy);
|
||||
integer i;
|
||||
reg [ADDR_WIDTH-1:0] base;
|
||||
reg [ADDR_WIDTH-1:0] width;
|
||||
reg [ADDR_WIDTH-1:0] size;
|
||||
reg [ADDR_WIDTH-1:0] mask;
|
||||
begin
|
||||
calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}};
|
||||
base = 0;
|
||||
for (i = 1; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32]) begin
|
||||
base = base + 2**M_ADDR_WIDTH[(i-1)*32 +: 32]; // increment
|
||||
base = base - (base % 2**M_ADDR_WIDTH[i*32 +: 32]); // align
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
width = M_ADDR_WIDTH[i*32 +: 32];
|
||||
mask = {ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - width);
|
||||
size = mask + 1;
|
||||
if (width > 0) begin
|
||||
if ((base & mask) != 0) begin
|
||||
base = base + size - (base & mask); // align
|
||||
end
|
||||
calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base;
|
||||
base = base + size; // increment
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -166,17 +174,51 @@ initial begin
|
||||
$display("Addressing configuration for axi_crossbar_addr instance %m");
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32]) begin
|
||||
$display("%2d (%2d): %x / %02d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %02d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if ((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & (2**M_ADDR_WIDTH[i*32 +: 32]-1)) != 0) begin
|
||||
$display("Region not aligned:");
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
$error("Error: address range not aligned (instance %m)");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin
|
||||
if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin
|
||||
if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))))
|
||||
&& ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin
|
||||
$display("Overlapping regions:");
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", j/M_REGIONS, j%M_REGIONS, M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[j*32 +: 32], M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
j/M_REGIONS, j%M_REGIONS,
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[j*32 +: 32],
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]),
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))
|
||||
);
|
||||
$error("Error: address ranges overlap (instance %m)");
|
||||
$finish;
|
||||
end
|
||||
|
@ -192,14 +192,22 @@ parameter AUSER_WIDTH = AWUSER_WIDTH > ARUSER_WIDTH ? AWUSER_WIDTH : ARUSER_WIDT
|
||||
function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy);
|
||||
integer i;
|
||||
reg [ADDR_WIDTH-1:0] base;
|
||||
reg [ADDR_WIDTH-1:0] width;
|
||||
reg [ADDR_WIDTH-1:0] size;
|
||||
reg [ADDR_WIDTH-1:0] mask;
|
||||
begin
|
||||
calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}};
|
||||
base = 0;
|
||||
for (i = 1; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32]) begin
|
||||
base = base + 2**M_ADDR_WIDTH[(i-1)*32 +: 32]; // increment
|
||||
base = base - (base % 2**M_ADDR_WIDTH[i*32 +: 32]); // align
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
width = M_ADDR_WIDTH[i*32 +: 32];
|
||||
mask = {ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - width);
|
||||
size = mask + 1;
|
||||
if (width > 0) begin
|
||||
if ((base & mask) != 0) begin
|
||||
base = base + size - (base & mask); // align
|
||||
end
|
||||
calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base;
|
||||
base = base + size; // increment
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -226,17 +234,51 @@ initial begin
|
||||
$display("Addressing configuration for axi_interconnect instance %m");
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32]) begin
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %02d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if ((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & (2**M_ADDR_WIDTH[i*32 +: 32]-1)) != 0) begin
|
||||
$display("Region not aligned:");
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
$error("Error: address range not aligned (instance %m)");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin
|
||||
if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin
|
||||
if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))))
|
||||
&& ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin
|
||||
$display("Overlapping regions:");
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", j/M_REGIONS, j%M_REGIONS, M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[j*32 +: 32], M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
j/M_REGIONS, j%M_REGIONS,
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[j*32 +: 32],
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]),
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))
|
||||
);
|
||||
$error("Error: address ranges overlap (instance %m)");
|
||||
$finish;
|
||||
end
|
||||
|
@ -100,14 +100,22 @@ parameter CL_M_COUNT = $clog2(M_COUNT);
|
||||
function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy);
|
||||
integer i;
|
||||
reg [ADDR_WIDTH-1:0] base;
|
||||
reg [ADDR_WIDTH-1:0] width;
|
||||
reg [ADDR_WIDTH-1:0] size;
|
||||
reg [ADDR_WIDTH-1:0] mask;
|
||||
begin
|
||||
calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}};
|
||||
base = 0;
|
||||
for (i = 1; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32]) begin
|
||||
base = base + 2**M_ADDR_WIDTH[(i-1)*32 +: 32]; // increment
|
||||
base = base - (base % 2**M_ADDR_WIDTH[i*32 +: 32]); // align
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
width = M_ADDR_WIDTH[i*32 +: 32];
|
||||
mask = {ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - width);
|
||||
size = mask + 1;
|
||||
if (width > 0) begin
|
||||
if ((base & mask) != 0) begin
|
||||
base = base + size - (base & mask); // align
|
||||
end
|
||||
calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base;
|
||||
base = base + size; // increment
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -131,20 +139,54 @@ initial begin
|
||||
end
|
||||
end
|
||||
|
||||
$display("Addressing configuration for axi_crossbar_addr instance %m");
|
||||
$display("Addressing configuration for axil_crossbar_addr instance %m");
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32]) begin
|
||||
$display("%2d (%2d): %x / %02d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %02d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if ((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & (2**M_ADDR_WIDTH[i*32 +: 32]-1)) != 0) begin
|
||||
$display("Region not aligned:");
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
$error("Error: address range not aligned (instance %m)");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin
|
||||
if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin
|
||||
if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))))
|
||||
&& ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin
|
||||
$display("Overlapping regions:");
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", j/M_REGIONS, j%M_REGIONS, M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[j*32 +: 32], M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
j/M_REGIONS, j%M_REGIONS,
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[j*32 +: 32],
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]),
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))
|
||||
);
|
||||
$error("Error: address ranges overlap (instance %m)");
|
||||
$finish;
|
||||
end
|
||||
|
@ -118,14 +118,22 @@ parameter CL_M_COUNT = $clog2(M_COUNT);
|
||||
function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy);
|
||||
integer i;
|
||||
reg [ADDR_WIDTH-1:0] base;
|
||||
reg [ADDR_WIDTH-1:0] width;
|
||||
reg [ADDR_WIDTH-1:0] size;
|
||||
reg [ADDR_WIDTH-1:0] mask;
|
||||
begin
|
||||
calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}};
|
||||
base = 0;
|
||||
for (i = 1; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32]) begin
|
||||
base = base + 2**M_ADDR_WIDTH[(i-1)*32 +: 32]; // increment
|
||||
base = base - (base % 2**M_ADDR_WIDTH[i*32 +: 32]); // align
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
width = M_ADDR_WIDTH[i*32 +: 32];
|
||||
mask = {ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - width);
|
||||
size = mask + 1;
|
||||
if (width > 0) begin
|
||||
if ((base & mask) != 0) begin
|
||||
base = base + size - (base & mask); // align
|
||||
end
|
||||
calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base;
|
||||
base = base + size; // increment
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -147,17 +155,51 @@ initial begin
|
||||
$display("Addressing configuration for axil_interconnect instance %m");
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32]) begin
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %02d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
if ((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & (2**M_ADDR_WIDTH[i*32 +: 32]-1)) != 0) begin
|
||||
$display("Region not aligned:");
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
$error("Error: address range not aligned (instance %m)");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin
|
||||
for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin
|
||||
if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin
|
||||
if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin
|
||||
if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))))
|
||||
&& ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin
|
||||
$display("Overlapping regions:");
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x", j/M_REGIONS, j%M_REGIONS, M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[j*32 +: 32], M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])));
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
i/M_REGIONS, i%M_REGIONS,
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[i*32 +: 32],
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]),
|
||||
M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))
|
||||
);
|
||||
$display("%2d (%2d): %x / %2d -- %x-%x",
|
||||
j/M_REGIONS, j%M_REGIONS,
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH],
|
||||
M_ADDR_WIDTH[j*32 +: 32],
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]),
|
||||
M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))
|
||||
);
|
||||
$error("Error: address ranges overlap (instance %m)");
|
||||
$finish;
|
||||
end
|
||||
|
154
fpga/lib/axi/rtl/axil_reg_if.v
Normal file
154
fpga/lib/axi/rtl/axil_reg_if.v
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2021 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI lite register interface module
|
||||
*/
|
||||
module axil_reg_if #
|
||||
(
|
||||
// Width of data bus in bits
|
||||
parameter DATA_WIDTH = 32,
|
||||
// Width of address bus in bits
|
||||
parameter ADDR_WIDTH = 32,
|
||||
// Width of wstrb (width of data bus in words)
|
||||
parameter STRB_WIDTH = (DATA_WIDTH/8),
|
||||
// Timeout delay (cycles)
|
||||
parameter TIMEOUT = 4
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* AXI-Lite slave interface
|
||||
*/
|
||||
input wire [ADDR_WIDTH-1:0] s_axil_awaddr,
|
||||
input wire [2:0] s_axil_awprot,
|
||||
input wire s_axil_awvalid,
|
||||
output wire s_axil_awready,
|
||||
input wire [DATA_WIDTH-1:0] s_axil_wdata,
|
||||
input wire [STRB_WIDTH-1:0] s_axil_wstrb,
|
||||
input wire s_axil_wvalid,
|
||||
output wire s_axil_wready,
|
||||
output wire [1:0] s_axil_bresp,
|
||||
output wire s_axil_bvalid,
|
||||
input wire s_axil_bready,
|
||||
input wire [ADDR_WIDTH-1:0] s_axil_araddr,
|
||||
input wire [2:0] s_axil_arprot,
|
||||
input wire s_axil_arvalid,
|
||||
output wire s_axil_arready,
|
||||
output wire [DATA_WIDTH-1:0] s_axil_rdata,
|
||||
output wire [1:0] s_axil_rresp,
|
||||
output wire s_axil_rvalid,
|
||||
input wire s_axil_rready,
|
||||
|
||||
/*
|
||||
* Register interface
|
||||
*/
|
||||
output wire [ADDR_WIDTH-1:0] reg_wr_addr,
|
||||
output wire [DATA_WIDTH-1:0] reg_wr_data,
|
||||
output wire [STRB_WIDTH-1:0] reg_wr_strb,
|
||||
output wire reg_wr_en,
|
||||
input wire reg_wr_wait,
|
||||
input wire reg_wr_ack,
|
||||
output wire [ADDR_WIDTH-1:0] reg_rd_addr,
|
||||
output wire reg_rd_en,
|
||||
input wire [DATA_WIDTH-1:0] reg_rd_data,
|
||||
input wire reg_rd_wait,
|
||||
input wire reg_rd_ack
|
||||
);
|
||||
|
||||
axil_reg_if_wr #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ADDR_WIDTH(ADDR_WIDTH),
|
||||
.STRB_WIDTH(STRB_WIDTH),
|
||||
.TIMEOUT(TIMEOUT)
|
||||
)
|
||||
axil_reg_if_wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI-Lite slave interface
|
||||
*/
|
||||
.s_axil_awaddr(s_axil_awaddr),
|
||||
.s_axil_awprot(s_axil_awprot),
|
||||
.s_axil_awvalid(s_axil_awvalid),
|
||||
.s_axil_awready(s_axil_awready),
|
||||
.s_axil_wdata(s_axil_wdata),
|
||||
.s_axil_wstrb(s_axil_wstrb),
|
||||
.s_axil_wvalid(s_axil_wvalid),
|
||||
.s_axil_wready(s_axil_wready),
|
||||
.s_axil_bresp(s_axil_bresp),
|
||||
.s_axil_bvalid(s_axil_bvalid),
|
||||
.s_axil_bready(s_axil_bready),
|
||||
|
||||
/*
|
||||
* Register interface
|
||||
*/
|
||||
.reg_wr_addr(reg_wr_addr),
|
||||
.reg_wr_data(reg_wr_data),
|
||||
.reg_wr_strb(reg_wr_strb),
|
||||
.reg_wr_en(reg_wr_en),
|
||||
.reg_wr_wait(reg_wr_wait),
|
||||
.reg_wr_ack(reg_wr_ack)
|
||||
);
|
||||
|
||||
axil_reg_if_rd #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ADDR_WIDTH(ADDR_WIDTH),
|
||||
.STRB_WIDTH(STRB_WIDTH),
|
||||
.TIMEOUT(TIMEOUT)
|
||||
)
|
||||
axil_reg_if_rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI-Lite slave interface
|
||||
*/
|
||||
.s_axil_araddr(s_axil_araddr),
|
||||
.s_axil_arprot(s_axil_arprot),
|
||||
.s_axil_arvalid(s_axil_arvalid),
|
||||
.s_axil_arready(s_axil_arready),
|
||||
.s_axil_rdata(s_axil_rdata),
|
||||
.s_axil_rresp(s_axil_rresp),
|
||||
.s_axil_rvalid(s_axil_rvalid),
|
||||
.s_axil_rready(s_axil_rready),
|
||||
|
||||
/*
|
||||
* Register interface
|
||||
*/
|
||||
.reg_rd_addr(reg_rd_addr),
|
||||
.reg_rd_en(reg_rd_en),
|
||||
.reg_rd_data(reg_rd_data),
|
||||
.reg_rd_wait(reg_rd_wait),
|
||||
.reg_rd_ack(reg_rd_ack)
|
||||
);
|
||||
|
||||
endmodule
|
132
fpga/lib/axi/rtl/axil_reg_if_rd.v
Normal file
132
fpga/lib/axi/rtl/axil_reg_if_rd.v
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2021 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI lite register interface module (read)
|
||||
*/
|
||||
module axil_reg_if_rd #
|
||||
(
|
||||
// Width of data bus in bits
|
||||
parameter DATA_WIDTH = 32,
|
||||
// Width of address bus in bits
|
||||
parameter ADDR_WIDTH = 32,
|
||||
// Width of wstrb (width of data bus in words)
|
||||
parameter STRB_WIDTH = (DATA_WIDTH/8),
|
||||
// Timeout delay (cycles)
|
||||
parameter TIMEOUT = 4
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* AXI-Lite slave interface
|
||||
*/
|
||||
input wire [ADDR_WIDTH-1:0] s_axil_araddr,
|
||||
input wire [2:0] s_axil_arprot,
|
||||
input wire s_axil_arvalid,
|
||||
output wire s_axil_arready,
|
||||
output wire [DATA_WIDTH-1:0] s_axil_rdata,
|
||||
output wire [1:0] s_axil_rresp,
|
||||
output wire s_axil_rvalid,
|
||||
input wire s_axil_rready,
|
||||
|
||||
/*
|
||||
* Register interface
|
||||
*/
|
||||
output wire [ADDR_WIDTH-1:0] reg_rd_addr,
|
||||
output wire reg_rd_en,
|
||||
input wire [DATA_WIDTH-1:0] reg_rd_data,
|
||||
input wire reg_rd_wait,
|
||||
input wire reg_rd_ack
|
||||
);
|
||||
|
||||
parameter TIMEOUT_WIDTH = $clog2(TIMEOUT);
|
||||
|
||||
reg [TIMEOUT_WIDTH-1:0] timeout_count_reg = 0, timeout_count_next;
|
||||
|
||||
reg [ADDR_WIDTH-1:0] s_axil_araddr_reg = {ADDR_WIDTH{1'b0}}, s_axil_araddr_next;
|
||||
reg s_axil_arvalid_reg = 1'b0, s_axil_arvalid_next;
|
||||
reg [DATA_WIDTH-1:0] s_axil_rdata_reg = {DATA_WIDTH{1'b0}}, s_axil_rdata_next;
|
||||
reg s_axil_rvalid_reg = 1'b0, s_axil_rvalid_next;
|
||||
|
||||
reg reg_rd_en_reg = 1'b0, reg_rd_en_next;
|
||||
|
||||
assign s_axil_arready = !s_axil_arvalid_reg;
|
||||
assign s_axil_rdata = s_axil_rdata_reg;
|
||||
assign s_axil_rresp = 2'b00;
|
||||
assign s_axil_rvalid = s_axil_rvalid_reg;
|
||||
|
||||
assign reg_rd_addr = s_axil_araddr_reg;
|
||||
assign reg_rd_en = reg_rd_en_reg;
|
||||
|
||||
always @* begin
|
||||
timeout_count_next = timeout_count_reg;
|
||||
|
||||
s_axil_araddr_next = s_axil_araddr_reg;
|
||||
s_axil_arvalid_next = s_axil_arvalid_reg;
|
||||
s_axil_rdata_next = s_axil_rdata_reg;
|
||||
s_axil_rvalid_next = s_axil_rvalid_reg && !s_axil_rready;
|
||||
|
||||
if (reg_rd_en_reg && (reg_rd_ack || timeout_count_reg == 0)) begin
|
||||
s_axil_arvalid_next = 1'b0;
|
||||
s_axil_rdata_next = reg_rd_data;
|
||||
s_axil_rvalid_next = 1'b1;
|
||||
end
|
||||
|
||||
if (!s_axil_arvalid_reg) begin
|
||||
s_axil_araddr_next = s_axil_araddr;
|
||||
s_axil_arvalid_next = s_axil_arvalid;
|
||||
timeout_count_next = TIMEOUT-1;
|
||||
end
|
||||
|
||||
if (reg_rd_en && !reg_rd_wait && timeout_count_reg != 0)begin
|
||||
timeout_count_next = timeout_count_reg - 1;
|
||||
end
|
||||
|
||||
reg_rd_en_next = s_axil_arvalid_next && !s_axil_rvalid_next;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
timeout_count_reg <= timeout_count_next;
|
||||
|
||||
s_axil_araddr_reg <= s_axil_araddr_next;
|
||||
s_axil_arvalid_reg <= s_axil_arvalid_next;
|
||||
s_axil_rdata_reg <= s_axil_rdata_next;
|
||||
s_axil_rvalid_reg <= s_axil_rvalid_next;
|
||||
|
||||
reg_rd_en_reg <= reg_rd_en_next;
|
||||
|
||||
if (rst) begin
|
||||
s_axil_arvalid_reg <= 1'b0;
|
||||
s_axil_rvalid_reg <= 1'b0;
|
||||
reg_rd_en_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
151
fpga/lib/axi/rtl/axil_reg_if_wr.v
Normal file
151
fpga/lib/axi/rtl/axil_reg_if_wr.v
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2021 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI lite register interface module (write)
|
||||
*/
|
||||
module axil_reg_if_wr #
|
||||
(
|
||||
// Width of data bus in bits
|
||||
parameter DATA_WIDTH = 32,
|
||||
// Width of address bus in bits
|
||||
parameter ADDR_WIDTH = 32,
|
||||
// Width of wstrb (width of data bus in words)
|
||||
parameter STRB_WIDTH = (DATA_WIDTH/8),
|
||||
// Timeout delay (cycles)
|
||||
parameter TIMEOUT = 4
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* AXI-Lite slave interface
|
||||
*/
|
||||
input wire [ADDR_WIDTH-1:0] s_axil_awaddr,
|
||||
input wire [2:0] s_axil_awprot,
|
||||
input wire s_axil_awvalid,
|
||||
output wire s_axil_awready,
|
||||
input wire [DATA_WIDTH-1:0] s_axil_wdata,
|
||||
input wire [STRB_WIDTH-1:0] s_axil_wstrb,
|
||||
input wire s_axil_wvalid,
|
||||
output wire s_axil_wready,
|
||||
output wire [1:0] s_axil_bresp,
|
||||
output wire s_axil_bvalid,
|
||||
input wire s_axil_bready,
|
||||
|
||||
/*
|
||||
* Register interface
|
||||
*/
|
||||
output wire [ADDR_WIDTH-1:0] reg_wr_addr,
|
||||
output wire [DATA_WIDTH-1:0] reg_wr_data,
|
||||
output wire [STRB_WIDTH-1:0] reg_wr_strb,
|
||||
output wire reg_wr_en,
|
||||
input wire reg_wr_wait,
|
||||
input wire reg_wr_ack
|
||||
);
|
||||
|
||||
parameter TIMEOUT_WIDTH = $clog2(TIMEOUT);
|
||||
|
||||
reg [TIMEOUT_WIDTH-1:0] timeout_count_reg = 0, timeout_count_next;
|
||||
|
||||
reg [ADDR_WIDTH-1:0] s_axil_awaddr_reg = {ADDR_WIDTH{1'b0}}, s_axil_awaddr_next;
|
||||
reg s_axil_awvalid_reg = 1'b0, s_axil_awvalid_next;
|
||||
reg [DATA_WIDTH-1:0] s_axil_wdata_reg = {DATA_WIDTH{1'b0}}, s_axil_wdata_next;
|
||||
reg [STRB_WIDTH-1:0] s_axil_wstrb_reg = {STRB_WIDTH{1'b0}}, s_axil_wstrb_next;
|
||||
reg s_axil_wvalid_reg = 1'b0, s_axil_wvalid_next;
|
||||
reg s_axil_bvalid_reg = 1'b0, s_axil_bvalid_next;
|
||||
|
||||
reg reg_wr_en_reg = 1'b0, reg_wr_en_next;
|
||||
|
||||
assign s_axil_awready = !s_axil_awvalid_reg;
|
||||
assign s_axil_wready = !s_axil_wvalid_reg;
|
||||
assign s_axil_bresp = 2'b00;
|
||||
assign s_axil_bvalid = s_axil_bvalid_reg;
|
||||
|
||||
assign reg_wr_addr = s_axil_awaddr_reg;
|
||||
assign reg_wr_data = s_axil_wdata_reg;
|
||||
assign reg_wr_strb = s_axil_wstrb_reg;
|
||||
assign reg_wr_en = reg_wr_en_reg;
|
||||
|
||||
always @* begin
|
||||
timeout_count_next = timeout_count_reg;
|
||||
|
||||
s_axil_awaddr_next = s_axil_awaddr_reg;
|
||||
s_axil_awvalid_next = s_axil_awvalid_reg;
|
||||
s_axil_wdata_next = s_axil_wdata_reg;
|
||||
s_axil_wstrb_next = s_axil_wstrb_reg;
|
||||
s_axil_wvalid_next = s_axil_wvalid_reg;
|
||||
s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready;
|
||||
|
||||
if (reg_wr_en_reg && (reg_wr_ack || timeout_count_reg == 0)) begin
|
||||
s_axil_awvalid_next = 1'b0;
|
||||
s_axil_wvalid_next = 1'b0;
|
||||
s_axil_bvalid_next = 1'b1;
|
||||
end
|
||||
|
||||
if (!s_axil_awvalid_reg) begin
|
||||
s_axil_awaddr_next = s_axil_awaddr;
|
||||
s_axil_awvalid_next = s_axil_awvalid;
|
||||
timeout_count_next = TIMEOUT-1;
|
||||
end
|
||||
|
||||
if (!s_axil_wvalid_reg) begin
|
||||
s_axil_wdata_next = s_axil_wdata;
|
||||
s_axil_wstrb_next = s_axil_wstrb;
|
||||
s_axil_wvalid_next = s_axil_wvalid;
|
||||
end
|
||||
|
||||
if (reg_wr_en && !reg_wr_wait && timeout_count_reg != 0)begin
|
||||
timeout_count_next = timeout_count_reg - 1;
|
||||
end
|
||||
|
||||
reg_wr_en_next = s_axil_awvalid_next && s_axil_wvalid_next && !s_axil_bvalid_next;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
timeout_count_reg <= timeout_count_next;
|
||||
|
||||
s_axil_awaddr_reg <= s_axil_awaddr_next;
|
||||
s_axil_awvalid_reg <= s_axil_awvalid_next;
|
||||
s_axil_wdata_reg <= s_axil_wdata_next;
|
||||
s_axil_wstrb_reg <= s_axil_wstrb_next;
|
||||
s_axil_wvalid_reg <= s_axil_wvalid_next;
|
||||
s_axil_bvalid_reg <= s_axil_bvalid_next;
|
||||
|
||||
reg_wr_en_reg <= reg_wr_en_next;
|
||||
|
||||
if (rst) begin
|
||||
s_axil_awvalid_reg <= 1'b0;
|
||||
s_axil_wvalid_reg <= 1'b0;
|
||||
s_axil_bvalid_reg <= 1'b0;
|
||||
reg_wr_en_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
80
fpga/lib/axi/tb/axil_reg_if/Makefile
Normal file
80
fpga/lib/axi/tb/axil_reg_if/Makefile
Normal file
@ -0,0 +1,80 @@
|
||||
# Copyright (c) 2021 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
export COCOTB_RESOLVE_X ?= RANDOM
|
||||
|
||||
DUT = axil_reg_if
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT)_rd.v
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT)_wr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 32
|
||||
export PARAM_ADDR_WIDTH ?= 16
|
||||
export PARAM_STRB_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_TIMEOUT ?= 4
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ADDR_WIDTH=$(PARAM_ADDR_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).STRB_WIDTH=$(PARAM_STRB_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TIMEOUT=$(PARAM_TIMEOUT)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GADDR_WIDTH=$(PARAM_ADDR_WIDTH)
|
||||
COMPILE_ARGS += -GSTRB_WIDTH=$(PARAM_STRB_WIDTH)
|
||||
COMPILE_ARGS += -GTIMEOUT=$(PARAM_TIMEOUT)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
304
fpga/lib/axi/tb/axil_reg_if/test_axil_reg_if.py
Normal file
304
fpga/lib/axi/tb/axil_reg_if/test_axil_reg_if.py
Normal file
@ -0,0 +1,304 @@
|
||||
"""
|
||||
|
||||
Copyright (c) 2021 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import mmap
|
||||
import os
|
||||
import random
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge, Timer
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiLiteBus, AxiLiteMaster
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 10, units="ns").start())
|
||||
|
||||
self.axil_master = AxiLiteMaster(AxiLiteBus.from_prefix(dut, "s_axil"), dut.clk, dut.rst)
|
||||
|
||||
dut.reg_wr_wait.setimmediatevalue(0)
|
||||
dut.reg_wr_ack.setimmediatevalue(0)
|
||||
dut.reg_rd_data.setimmediatevalue(0)
|
||||
dut.reg_rd_wait.setimmediatevalue(0)
|
||||
dut.reg_rd_ack.setimmediatevalue(0)
|
||||
|
||||
self.mem = mmap.mmap(-1, 16384)
|
||||
|
||||
cocotb.fork(self.run_reg_read())
|
||||
cocotb.fork(self.run_reg_write())
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axil_master.write_if.aw_channel.set_pause_generator(generator())
|
||||
self.axil_master.write_if.w_channel.set_pause_generator(generator())
|
||||
self.axil_master.read_if.ar_channel.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axil_master.write_if.b_channel.set_pause_generator(generator())
|
||||
self.axil_master.read_if.r_channel.set_pause_generator(generator())
|
||||
|
||||
async def cycle_reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
async def run_reg_read(self):
|
||||
byte_lanes = len(self.dut.reg_wr_strb)
|
||||
|
||||
while True:
|
||||
self.dut.reg_rd_data <= 0
|
||||
self.dut.reg_rd_wait <= 0
|
||||
self.dut.reg_rd_ack <= 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
addr = (self.dut.reg_rd_addr.value.integer // byte_lanes) * byte_lanes
|
||||
|
||||
if self.dut.reg_rd_en.value.integer and addr < len(self.mem):
|
||||
self.dut.reg_rd_wait <= 1
|
||||
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
self.mem.seek(addr)
|
||||
|
||||
data = self.mem.read(byte_lanes)
|
||||
|
||||
self.dut.reg_rd_data <= int.from_bytes(data, 'little')
|
||||
self.dut.reg_rd_wait <= 0
|
||||
self.dut.reg_rd_ack <= 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
async def run_reg_write(self):
|
||||
byte_lanes = len(self.dut.reg_wr_strb)
|
||||
|
||||
while True:
|
||||
self.dut.reg_wr_wait <= 0
|
||||
self.dut.reg_wr_ack <= 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
addr = (self.dut.reg_wr_addr.value.integer // byte_lanes) * byte_lanes
|
||||
data = self.dut.reg_wr_data.value.integer
|
||||
strb = self.dut.reg_wr_strb.value.integer
|
||||
|
||||
if self.dut.reg_wr_en.value.integer and addr < len(self.mem):
|
||||
self.dut.reg_wr_wait <= 1
|
||||
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
self.mem.seek(addr)
|
||||
|
||||
data = data.to_bytes(byte_lanes, 'little')
|
||||
|
||||
for i in range(byte_lanes):
|
||||
if strb & (1 << i):
|
||||
self.mem.write(data[i:i+1])
|
||||
else:
|
||||
self.mem.seek(1, 1)
|
||||
|
||||
self.dut.reg_wr_wait <= 0
|
||||
self.dut.reg_wr_ack <= 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
def mem_read(self, address, length):
|
||||
self.mem.seek(address)
|
||||
return self.mem.read(length)
|
||||
|
||||
def mem_write(self, address, data):
|
||||
self.mem.seek(address)
|
||||
self.mem.write(bytes(data))
|
||||
|
||||
|
||||
async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_inserter=None):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in range(1, byte_lanes*2):
|
||||
for offset in range(byte_lanes):
|
||||
tb.log.info("length %d, offset %d", length, offset)
|
||||
addr = offset+0x100
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.mem_write(addr-128, b'\xaa'*(length+256))
|
||||
|
||||
await tb.axil_master.write(addr, test_data)
|
||||
|
||||
tb.log.debug("%s", tb.mem_read((addr & ~0xf)-16, (((addr & 0xf)+length-1) & ~0xf)+48))
|
||||
|
||||
assert tb.mem_read(addr, length) == test_data
|
||||
assert tb.mem_read(addr-1, 1) == b'\xaa'
|
||||
assert tb.mem_read(addr+length, 1) == b'\xaa'
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inserter=None):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in range(1, byte_lanes*2):
|
||||
for offset in range(byte_lanes):
|
||||
tb.log.info("length %d, offset %d", length, offset)
|
||||
addr = offset+0x100
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.mem_write(addr, test_data)
|
||||
|
||||
data = await tb.axil_master.read(addr, length)
|
||||
|
||||
assert data.data == test_data
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
async def worker(master, offset, aperture, count=16):
|
||||
for k in range(count):
|
||||
length = random.randint(1, min(32, aperture))
|
||||
addr = offset+random.randint(0, aperture-length)
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
await master.write(addr, test_data)
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
data = await master.read(addr, length)
|
||||
assert data.data == test_data
|
||||
|
||||
workers = []
|
||||
|
||||
for k in range(16):
|
||||
workers.append(cocotb.fork(worker(tb.axil_master, k*0x100, 0x100, count=16)))
|
||||
|
||||
while workers:
|
||||
await workers.pop(0).join()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def cycle_pause():
|
||||
return itertools.cycle([1, 1, 1, 0])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_write, run_test_read]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("idle_inserter", [None, cycle_pause])
|
||||
factory.add_option("backpressure_inserter", [None, cycle_pause])
|
||||
factory.generate_tests()
|
||||
|
||||
factory = TestFactory(run_stress_test)
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data_width", [8, 16, 32])
|
||||
def test_axil_reg_if(request, data_width):
|
||||
dut = "axil_reg_if"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, f"{dut}_rd.v"),
|
||||
os.path.join(rtl_dir, f"{dut}_wr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['ADDR_WIDTH'] = 16
|
||||
parameters['STRB_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['TIMEOUT'] = 4
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
extra_env['COCOTB_RESOLVE_X'] = 'RANDOM'
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user