Add AXIS-Wishbone bridge
This adds the core of the UART-Wishbone bridge. The protocol has a variable-length address phase to help reduce overhead. Multiple in-flight commands are not supported, although this could be resolved with some FIFOs. Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
parent
0f6d4b166f
commit
75142311f2
|
@ -18,3 +18,6 @@ __pycache__
|
|||
*.fst
|
||||
results.xml
|
||||
test_profile.pstat
|
||||
|
||||
# doc artifacts
|
||||
*.html
|
||||
|
|
14
Makefile
14
Makefile
|
@ -2,6 +2,7 @@
|
|||
# Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
|
||||
|
||||
Q = 1
|
||||
ADOC = asciidoctor
|
||||
SYNTH = yosys
|
||||
PNR = nextpnr-ice40
|
||||
ICARUS = iverilog
|
||||
|
@ -124,6 +125,7 @@ endef
|
|||
|
||||
MODULES += axis_replay_buffer
|
||||
MODULES += axis_mii_tx
|
||||
MODULES += axis_wb_bridge
|
||||
MODULES += descramble
|
||||
MODULES += hub
|
||||
MODULES += hub_core
|
||||
|
@ -153,6 +155,17 @@ test: $(addsuffix .fst,$(MODULES)) $(addsuffix .synth.fst,$(MODULES))
|
|||
.PHONY: asc
|
||||
asc: $(addprefix rtl/,$(addsuffix .asc,$(MODULES)))
|
||||
|
||||
doc/output:
|
||||
mkdir -p $@
|
||||
|
||||
doc/output/%.html: doc/%.adoc doc/docinfo.html | doc/output
|
||||
$(ADOC) -o $@ $<
|
||||
|
||||
DOCS += uart_wb_bridge
|
||||
|
||||
.PHONY: htmldocs
|
||||
htmldocs: $(addprefix doc/output/,$(addsuffix .html,$(DOCS)))
|
||||
|
||||
CLEAN_EXT := .json .asc .pre .vvp .d .synth.v .place.v .sdf .bin
|
||||
|
||||
.PHONY: clean
|
||||
|
@ -161,3 +174,4 @@ clean:
|
|||
rm -rf log
|
||||
rm -f $(addprefix rtl/*,$(CLEAN_EXT))
|
||||
rm -f $(addprefix examples/*/*,$(CLEAN_EXT))
|
||||
rm -rf doc/output
|
||||
|
|
|
@ -77,6 +77,15 @@ preamble/SFD and appends a 4-byte FCS to the data. It currently only supports
|
|||
100M ethernet, although 10M would be easy to add. I have no plans to support
|
||||
1000M.
|
||||
|
||||
=== `axis_wb_bridge`
|
||||
|
||||
This module implements an AXI Stream to Wishbone bridge. This is not a
|
||||
more-typical DMA bridge, where streaming data is written in a fixed pattern.
|
||||
Rather, this module allows interactive or scripted examination and readout
|
||||
of a Wishbone bus. For more details on the protocol implemented by this bridge,
|
||||
refer to the xref:doc/uart_wb_bridge.adoc#protocol[UART-Wishbone Bridge
|
||||
documentation].
|
||||
|
||||
=== `descramble`
|
||||
|
||||
This implements a descrambler as specified in ANSI X3.264-1995 section 7.2.3. It
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/wavedrom/3.1.0/skins/default.js" type="text/javascript"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/wavedrom/3.1.0/wavedrom.min.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">document.addEventListener('DOMContentLoaded', WaveDrom.ProcessAll)</script>
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
= UART-Wishbone Bridge
|
||||
:docinfo: shared
|
||||
|
||||
[[protocol]]
|
||||
== Protocol
|
||||
|
||||
The following sections outline the protocol used to communicate with the UART
|
||||
half of the bridge.
|
||||
|
||||
=== Overview
|
||||
|
||||
The UART protocol uses a request/response format. Each wishbone transaction
|
||||
corresponds to one request and one response. Each request begins with a command
|
||||
byte; an optional, variable-length address; and some data if the request is a
|
||||
write. Each response begins with a status byte, followed by some data if the
|
||||
request was a read. The following diagram shows a successful read:
|
||||
|
||||
++++
|
||||
<script type="WaveDrom">
|
||||
{ signal : [
|
||||
{ name: "rx", wave: "z34444z....", data: "CMD ADDR0 ADDR1 ADDR2 ADDR3" },
|
||||
{ name: "tx", wave: "z......655z", data: "STATUS DATA0 DATA1" },
|
||||
],
|
||||
config: { hscale: 2 },
|
||||
}
|
||||
</script>
|
||||
++++
|
||||
|
||||
Similarly, this diagram shows a successful write:
|
||||
|
||||
++++
|
||||
<script type="WaveDrom">
|
||||
{ signal : [
|
||||
{ name: "rx", wave: "z3444455z..", data: "CMD ADDR0 ADDR1 ADDR2 ADDR3 DATA0 DATA1" },
|
||||
{ name: "tx", wave: "z........6z", data: "RESP" },
|
||||
],
|
||||
config: { hscale: 2 },
|
||||
}
|
||||
</script>
|
||||
++++
|
||||
|
||||
The bridge contains an internal address register that retains its state between
|
||||
different transactions. It possible to reduce the length of requests by
|
||||
partially modifying the address register.
|
||||
|
||||
=== Requests
|
||||
|
||||
Each request begins with a command byte. The format of the command byte is as
|
||||
follows:
|
||||
|
||||
.Command byte
|
||||
[cols="1,1,4a"]
|
||||
|===
|
||||
| Bit | Name | Description
|
||||
|
||||
| 0 | Clear | Setting this bit clears the address register before modifying
|
||||
it. The address register should always be cleared during the
|
||||
first transaction following a reset.
|
||||
| 1 | Write-Enable | If this bit is set, this request is a write, and a data
|
||||
phase follows the address phase. Otherwise, this request
|
||||
is a read, and there is no data phase.
|
||||
| 2 | Post-Increment | If this bit is set, the address register will be
|
||||
incremented when the transaction completes.
|
||||
| 4:3 | Address length
|
||||
| This field indicates the number of bytes in the subsequent address phase.
|
||||
!===
|
||||
! Value ! Address bytes
|
||||
|
||||
! 0 ! 0 (no address phase)
|
||||
! 1 ! 1
|
||||
! 2 ! 2
|
||||
! 3 ! 4
|
||||
!===
|
||||
| 7:5 | Reserved | Reserved, set to 0.
|
||||
|===
|
||||
|
||||
Following the command byte, there is an optional address phase. The length of
|
||||
the address phase is determined by the command byte. Bytes in the address phase
|
||||
are loaded into the address register. The address is transmitted in big-endian
|
||||
byte order (most-significant byte first). If number of bytes in the address
|
||||
phase is smaller than the size of the address register, the lower bytes in the
|
||||
address register will be replaced, and the upper bytes will not be modified.
|
||||
|
||||
The following table shows the value of each byte in the address register after a
|
||||
particular address phase. Bytes are numbered by the order they are transmitted:
|
||||
|
||||
.Address phase
|
||||
|===
|
||||
| Address bytes | Address[31:24] | Address[23:16] | Address[15:8] | Address[7:0]
|
||||
|
||||
| 0 | Unmodified | Unmodified | Unmodified | Unmodifed
|
||||
| 1 | Unmodified | Unmodified | Unmodified | Byte 0
|
||||
| 2 | Unmodified | Unmodified | Byte 0 | Byte 1
|
||||
| 4 | Byte 0 | Byte 1 | Byte 2 | Byte 3
|
||||
|===
|
||||
|
||||
Finally, there is a data phase if the request is a write. Data is transmitted
|
||||
in big-endian byte order (most-significant byte first).
|
||||
|
||||
Any requests transmitted while the bridge is processing another request will not
|
||||
be handled correctly. This condition is indicated by an overflow status in
|
||||
response to the initial request.
|
||||
|
||||
=== Responses
|
||||
|
||||
Each response begins with a status byte. The format of the status byte is as
|
||||
follows:
|
||||
|
||||
.Status byte
|
||||
[cols="1,1,4a"]
|
||||
|===
|
||||
| Bit | Name | Description
|
||||
|
||||
| 0 | Write Response | If set, the response is for a write and no data phase
|
||||
follows. Otherwise, the response is for a read and a data
|
||||
phase will follow.
|
||||
| 1 | Bus Error | There was bus error when servicing the request, and no data
|
||||
phase will follow. This bit has priority over any data phase
|
||||
implied by the Write Response bit.
|
||||
| 2 | Reserved | Reserved, do not use.
|
||||
| 3 | Overflow | While processing this request, the receive UART overflowed, and
|
||||
one or more request bytes were dropped. The bridge must be
|
||||
reset before issuing the next command.
|
||||
| 7:4 | Reserved | Reserved, do not use.
|
||||
|===
|
||||
|
||||
Finally, there is a data phase if the request was a read. Data is transmitted
|
||||
in big-endian byte order (most-significant byte first).
|
||||
|
||||
=== Resetting
|
||||
|
||||
The bridge and wishbone bus may be reset by sending a character with a framing
|
||||
error (a break) over the serial line. The bridge should be reset before each
|
||||
session in order to bring the bridge into a known state.
|
||||
|
||||
=== Examples
|
||||
|
||||
A read of `0xcafe` from `0x00000123` followed by a write of `0xbabe` to the same
|
||||
address:
|
||||
|
||||
++++
|
||||
<script type="WaveDrom">
|
||||
{ signal : [
|
||||
{ name: "rx", wave: "z344z....355z..", data: "11 01 23 02 ba be" },
|
||||
{ name: "tx", wave: "z....655z....6z", data: "00 ca fe 01" },
|
||||
]}
|
||||
</script>
|
||||
++++
|
||||
|
||||
Reading from `0x80001000`, `0x80002000`, and `0x80002001`:
|
||||
|
||||
++++
|
||||
<script type="WaveDrom">
|
||||
{ signal : [
|
||||
{ name: "rx", wave: "z34444z....344z....3z....", data: "18 80 00 10 00 14 20 00 00" },
|
||||
{ name: "tx", wave: "z......655z....655z..655z", data: "00 d0 0d 00 fe ed 00 fa ce" },
|
||||
]}
|
||||
</script>
|
||||
++++
|
|
@ -0,0 +1,210 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-Only
|
||||
/*
|
||||
* Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
|
||||
*/
|
||||
|
||||
`include "common.vh"
|
||||
|
||||
module axis_wb_bridge (
|
||||
input clk,
|
||||
input rst,
|
||||
|
||||
output reg s_axis_ready,
|
||||
input s_axis_valid,
|
||||
input [7:0] s_axis_data,
|
||||
|
||||
input m_axis_ready,
|
||||
output reg m_axis_valid,
|
||||
output reg [7:0] m_axis_data,
|
||||
|
||||
/* Wishbone */
|
||||
input wb_ack, wb_err,
|
||||
output reg wb_cyc, wb_stb, wb_we,
|
||||
output reg [ADDR_WIDTH - 1:0] wb_addr,
|
||||
output reg [15:0] wb_data_write,
|
||||
input [15:0] wb_data_read,
|
||||
|
||||
input overflow
|
||||
);
|
||||
|
||||
parameter ADDR_WIDTH = 32;
|
||||
generate if (ADDR_WIDTH % 8)
|
||||
$error("Unsupported ADDR_WIDTH");
|
||||
endgenerate
|
||||
/* The data width is not parametric for now */
|
||||
localparam DATA_WIDTH = 16;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam ADDR3 = 1;
|
||||
localparam ADDR2 = 2;
|
||||
localparam ADDR1 = 3;
|
||||
localparam ADDR0 = 4;
|
||||
localparam DATA1 = 5;
|
||||
localparam DATA0 = 6;
|
||||
localparam BUS = 7;
|
||||
localparam RESP2 = 8;
|
||||
localparam RESP1 = 9;
|
||||
localparam RESP0 = 10;
|
||||
|
||||
reg s_axis_ready_next, s_axis_valid_last, m_axis_ready_last, m_axis_valid_next;
|
||||
reg [7:0] s_axis_data_last, m_axis_data_next;
|
||||
reg wb_ack_last, wb_err_last;
|
||||
reg wb_stb_next, wb_we_next;
|
||||
reg [ADDR_WIDTH - 1:0] wb_addr_next;
|
||||
reg [15:0] wb_data_write_next, wb_data_latch, wb_data_latch_next;
|
||||
reg [3:0] state, state_next;
|
||||
reg overflow_latch, overflow_latch_next, postinc, postinc_next;
|
||||
|
||||
always @(*) begin
|
||||
s_axis_ready_next = s_axis_ready;
|
||||
m_axis_valid_next = m_axis_valid;
|
||||
m_axis_data_next = 8'bX;
|
||||
|
||||
wb_cyc = wb_stb;
|
||||
wb_stb_next = wb_stb;
|
||||
wb_we_next = wb_we;
|
||||
wb_addr_next = wb_addr;
|
||||
wb_data_write_next = wb_data_write;
|
||||
if (wb_stb && (wb_err || wb_ack))
|
||||
wb_data_latch_next = wb_data_read;
|
||||
else
|
||||
wb_data_latch_next = wb_data_latch;
|
||||
|
||||
state_next = state;
|
||||
postinc_next = postinc;
|
||||
overflow_latch_next = overflow_latch || overflow;
|
||||
|
||||
case (state)
|
||||
IDLE: if (s_axis_valid_last) begin
|
||||
if (s_axis_data_last[0])
|
||||
wb_addr_next = {ADDR_WIDTH{1'b0}};
|
||||
wb_we_next = s_axis_data_last[1];
|
||||
postinc_next = s_axis_data_last[2];
|
||||
case (s_axis_data_last[4:3])
|
||||
2'd3: state_next = ADDR3;
|
||||
2'd2: state_next = ADDR1;
|
||||
2'd1: state_next = ADDR0;
|
||||
2'd0: if (wb_we_next) begin
|
||||
state_next = DATA1;
|
||||
end else begin
|
||||
state_next = BUS;
|
||||
wb_stb_next = 1;
|
||||
s_axis_ready_next = 0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
ADDR3: if (s_axis_valid_last) begin
|
||||
if (ADDR_WIDTH >= 32)
|
||||
wb_addr_next[31:24] = s_axis_data_last;
|
||||
state_next = ADDR2;
|
||||
end
|
||||
ADDR2: if (s_axis_valid_last) begin
|
||||
if (ADDR_WIDTH >= 24)
|
||||
wb_addr_next[23:16] = s_axis_data_last;
|
||||
state_next = ADDR1;
|
||||
end
|
||||
ADDR1: if (s_axis_valid_last) begin
|
||||
if (ADDR_WIDTH >= 16)
|
||||
wb_addr_next[15:8] = s_axis_data_last;
|
||||
state_next = ADDR0;
|
||||
end
|
||||
ADDR0: if (s_axis_valid_last) begin
|
||||
if (ADDR_WIDTH >= 8)
|
||||
wb_addr_next[7:0] = s_axis_data_last;
|
||||
if (wb_we) begin
|
||||
state_next = DATA1;
|
||||
end else begin
|
||||
state_next = BUS;
|
||||
wb_stb_next = 1;
|
||||
s_axis_ready_next = 0;
|
||||
end
|
||||
end
|
||||
DATA1: if (s_axis_valid_last) begin
|
||||
wb_data_write_next = { wb_data_write[7:0], s_axis_data_last };
|
||||
state_next = DATA0;
|
||||
end
|
||||
DATA0: if(s_axis_valid_last) begin
|
||||
wb_data_write_next = { wb_data_write[7:0], s_axis_data_last };
|
||||
state_next = BUS;
|
||||
wb_stb_next = 1;
|
||||
s_axis_ready_next = 0;
|
||||
end
|
||||
BUS: if (wb_ack || wb_err) begin
|
||||
wb_stb_next = 0;
|
||||
wb_addr_next[7:0] = wb_addr[7:0] + postinc;
|
||||
m_axis_valid_next = 1;
|
||||
m_axis_data_next = { 4'b0, overflow_latch_next, 1'b0, wb_err, wb_we };
|
||||
overflow_latch_next = 0;
|
||||
state_next = wb_we || wb_err ? RESP0 : RESP2;
|
||||
end
|
||||
RESP2: if (m_axis_ready_last) begin
|
||||
m_axis_data_next = wb_data_latch[15:8];
|
||||
state_next = RESP1;
|
||||
end
|
||||
RESP1: if (m_axis_ready_last) begin
|
||||
m_axis_data_next = wb_data_latch[7:0];
|
||||
state_next = RESP0;
|
||||
end
|
||||
RESP0: if (m_axis_ready_last) begin
|
||||
m_axis_valid_next = 0;
|
||||
s_axis_ready_next = 1;
|
||||
state_next = IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
s_axis_data_last <= s_axis_data;
|
||||
m_axis_data <= m_axis_data_next;
|
||||
wb_we <= wb_we_next;
|
||||
wb_addr <= wb_addr_next;
|
||||
wb_data_write <= wb_data_write_next;
|
||||
wb_data_latch <= wb_data_latch_next;
|
||||
postinc <= postinc_next;
|
||||
end
|
||||
|
||||
always @(posedge clk, posedge rst) begin
|
||||
if (rst) begin
|
||||
s_axis_ready <= 1;
|
||||
s_axis_valid_last <= 0;
|
||||
m_axis_ready_last <= 0;
|
||||
m_axis_valid <= 0;
|
||||
wb_ack_last <= 0;
|
||||
wb_err_last <= 0;
|
||||
wb_stb <= 0;
|
||||
state <= IDLE;
|
||||
overflow_latch <= 0;
|
||||
end else begin
|
||||
s_axis_ready <= s_axis_ready_next;
|
||||
s_axis_valid_last <= s_axis_valid;
|
||||
m_axis_ready_last <= m_axis_ready;
|
||||
m_axis_valid <= m_axis_valid_next;
|
||||
wb_ack_last <= wb_ack;
|
||||
wb_err_last <= wb_err;
|
||||
wb_stb <= wb_stb_next;
|
||||
state <= state_next;
|
||||
overflow_latch <= overflow_latch_next;
|
||||
end
|
||||
end
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
reg [255:0] state_text;
|
||||
|
||||
always @(*) begin
|
||||
case (state)
|
||||
IDLE: state_text = "IDLE";
|
||||
ADDR3: state_text = "ADDR3";
|
||||
ADDR2: state_text = "ADDR2";
|
||||
ADDR1: state_text = "ADDR1";
|
||||
ADDR0: state_text = "ADDR0";
|
||||
DATA1: state_text = "DATA1";
|
||||
DATA0: state_text = "DATA0";
|
||||
BUS: state_text = "BUS";
|
||||
RESP2: state_text = "RESP2";
|
||||
RESP1: state_text = "RESP1";
|
||||
RESP0: state_text = "RESP0";
|
||||
endcase
|
||||
end
|
||||
`endif
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,166 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-Only
|
||||
# Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
|
||||
|
||||
import cocotb
|
||||
from cocotb.binary import BinaryValue
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import FallingEdge, Timer
|
||||
|
||||
from .axis_replay_buffer import send_packet, recv_packet
|
||||
from .mdio import wb_read, wb_write, wb_err
|
||||
from .util import BIT, GENMASK
|
||||
|
||||
CMD_CLEAR = BIT(0)
|
||||
CMD_WE = BIT(1)
|
||||
CMD_POSTINC = BIT(2)
|
||||
CMD_ADDR0 = 0x00
|
||||
CMD_ADDR8 = 0x08
|
||||
CMD_ADDR16 = 0x10
|
||||
CMD_ADDR32 = 0x18
|
||||
|
||||
STATUS_WE = BIT(0)
|
||||
STATUS_ERR = BIT(1)
|
||||
STATUS_OVERFLOW = BIT(3)
|
||||
|
||||
class Encoder:
|
||||
def __init__(self):
|
||||
self.last_addr = None
|
||||
|
||||
def encode(self, addr, data=None, postinc=False):
|
||||
cmd = CMD_POSTINC if postinc else 0
|
||||
if data is None:
|
||||
data_bytes = ()
|
||||
else:
|
||||
cmd |= CMD_WE
|
||||
data_bytes = data.to_bytes(2, 'big')
|
||||
|
||||
if self.last_addr is None:
|
||||
cmd |= CMD_CLEAR
|
||||
self.last_addr = 0
|
||||
|
||||
def addr_len(last):
|
||||
if (addr ^ last) & ~GENMASK(15, 0):
|
||||
return 4
|
||||
if (addr ^ last) & GENMASK(15, 8):
|
||||
return 2
|
||||
if (addr ^ last) & GENMASK(7, 0):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
len_zero = addr_len(0)
|
||||
len_last = addr_len(self.last_addr)
|
||||
if len_zero < len_last:
|
||||
addr_len = len_zero
|
||||
cmd |= CMD_CLEAR
|
||||
else:
|
||||
addr_len = len_last
|
||||
|
||||
addr_bytes = (addr & GENMASK(addr_len * 8 - 1, 0)).to_bytes(addr_len, 'big')
|
||||
if addr_len == 4:
|
||||
cmd |= CMD_ADDR32
|
||||
elif addr_len == 2:
|
||||
cmd |= CMD_ADDR16
|
||||
elif addr_len == 1:
|
||||
cmd |= CMD_ADDR8
|
||||
else:
|
||||
cmd |= CMD_ADDR0
|
||||
|
||||
self.last_addr = (addr & ~GENMASK(7, 0)) | ((addr + postinc) & GENMASK(7, 0))
|
||||
return (cmd, *addr_bytes, *data_bytes)
|
||||
|
||||
@cocotb.test(timeout_time=10, timeout_unit='us')
|
||||
async def test_bridge(bridge):
|
||||
bridge.clk.value = BinaryValue('Z')
|
||||
bridge.rst.value = 1
|
||||
bridge.s_axis_valid.value = 0
|
||||
bridge.m_axis_ready.value = 1
|
||||
bridge.wb_ack.value = 0
|
||||
bridge.wb_err.value = 0
|
||||
bridge.overflow.value = 0
|
||||
|
||||
await Timer(1)
|
||||
bridge.rst.value = 0
|
||||
await cocotb.start(Clock(bridge.clk, 8, units='ns').start())
|
||||
await FallingEdge(bridge.clk)
|
||||
|
||||
s_axis = {
|
||||
'clk': bridge.clk,
|
||||
'ready': bridge.s_axis_ready,
|
||||
'valid': bridge.s_axis_valid,
|
||||
'data': bridge.s_axis_data,
|
||||
}
|
||||
|
||||
m_axis = {
|
||||
'clk': bridge.clk,
|
||||
'ready': bridge.m_axis_ready,
|
||||
'valid': bridge.m_axis_valid,
|
||||
'data': bridge.m_axis_data,
|
||||
}
|
||||
|
||||
wb = {
|
||||
'clk': bridge.clk,
|
||||
'ack': bridge.wb_ack,
|
||||
'err': bridge.wb_err,
|
||||
'cyc': bridge.wb_cyc,
|
||||
'stb': bridge.wb_stb,
|
||||
'we': bridge.wb_we,
|
||||
'addr': bridge.wb_addr,
|
||||
'data_write': bridge.wb_data_write,
|
||||
'data_read': bridge.wb_data_read,
|
||||
}
|
||||
|
||||
e = Encoder()
|
||||
|
||||
async def read(addr, data, postinc=False, resp=0):
|
||||
await send_packet(s_axis, e.encode(addr, None, postinc))
|
||||
|
||||
bridge.overflow.value = bool(resp & STATUS_OVERFLOW)
|
||||
if resp & STATUS_ERR:
|
||||
await wb_err(wb)
|
||||
bridge.overflow.value = 0
|
||||
await recv_packet(m_axis, (resp,))
|
||||
else:
|
||||
await wb_read(wb, addr, data)
|
||||
bridge.overflow.value = 0
|
||||
await recv_packet(m_axis, (resp, *data.to_bytes(2, 'big')))
|
||||
|
||||
async def write(addr, data, postinc=False, resp=STATUS_WE):
|
||||
await send_packet(s_axis, e.encode(addr, data, postinc))
|
||||
|
||||
bridge.overflow.value = bool(resp & STATUS_OVERFLOW)
|
||||
if resp & STATUS_ERR:
|
||||
await wb_err(wb)
|
||||
else:
|
||||
await wb_write(wb, addr, data)
|
||||
bridge.overflow.value = 0
|
||||
|
||||
await recv_packet(m_axis, (resp,))
|
||||
|
||||
for f in read, write:
|
||||
await f(0x01234567, 0x89ab)
|
||||
await f(0x01234567, 0xcdef)
|
||||
await f(0x012345fe, 1, True)
|
||||
await f(0x012345ff, 2, True)
|
||||
await f(0x01234500, 3)
|
||||
await f(0x012345ff, 4, True)
|
||||
await f(0x01234600, 5)
|
||||
await f(0x0123ffff, 6)
|
||||
await f(0x01ffffff, 7)
|
||||
await f(0xffffffff, 8)
|
||||
await f(0x0000ffff, 9)
|
||||
await f(0x000000ff, 10)
|
||||
await f(0x00000000, 11)
|
||||
|
||||
# fast back-to-back
|
||||
recv = await cocotb.start(recv_packet(m_axis, (STATUS_WE, 0, 0, 4)))
|
||||
await send_packet(s_axis, e.encode(1, 2))
|
||||
await wb_write(wb, 1, 2)
|
||||
await send_packet(s_axis, e.encode(3))
|
||||
await wb_read(wb, 3, 4)
|
||||
await recv
|
||||
|
||||
# bus error/overflow
|
||||
await write(5, 6, resp=STATUS_WE | STATUS_ERR | STATUS_OVERFLOW)
|
||||
await read(7, 8, resp=STATUS_ERR)
|
||||
await read(9, 10, resp=STATUS_OVERFLOW)
|
||||
await write(11, 12)
|
Loading…
Reference in New Issue