Add a basic hub
This adds a basic clause 27 repeater (hub), mostly for test purposes. It's effectively just the state machine in figure 27-4 and nothing else (e.g. no partitioning or jabber detection). This is surprisingly simple. Unfortunately, yosys doesn't allow memories in port declarations, even for systemverilog. This complicates the implementation and testbench, since we have to do the slicing ourselves. This is particularly awful for the testbench, since module.signal[0].value != module.signal.value[0] and module.signal can't be indexed by slices, and module.signal.value is big endian (ugh ugh ugh). There is no clean solution here. Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
parent
798968d3d6
commit
b68e1312c4
|
@ -0,0 +1,68 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-Only
|
||||
/*
|
||||
* Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
|
||||
*/
|
||||
|
||||
`include "common.vh"
|
||||
`include "io.vh"
|
||||
|
||||
module hub_core (
|
||||
input clk,
|
||||
|
||||
/* MII */
|
||||
input [PORT_COUNT - 1:0] rx_dv,
|
||||
input [PORT_COUNT - 1:0] rx_er,
|
||||
input [PORT_COUNT * 4 - 1:0] rxd,
|
||||
|
||||
output reg [PORT_COUNT - 1:0] tx_en,
|
||||
output reg [PORT_COUNT - 1:0] tx_er,
|
||||
output reg [PORT_COUNT * 4 - 1:0] txd
|
||||
);
|
||||
|
||||
parameter PORT_COUNT = 4;
|
||||
localparam PORT_BITS = $clog2(PORT_COUNT);
|
||||
|
||||
localparam DATA_JAM = 4'h5;
|
||||
|
||||
integer i;
|
||||
reg jam, activity;
|
||||
reg [PORT_BITS - 1:0] active_port;
|
||||
reg [PORT_COUNT - 1:0] tx_en_next, tx_er_next;
|
||||
reg [3:0] txd_next [PORT_COUNT - 1:0];
|
||||
|
||||
always @(*) begin
|
||||
jam = 0;
|
||||
activity = 0;
|
||||
active_port = {PORT_BITS{1'bx}};
|
||||
for (i = 0; i < PORT_COUNT; i = i + 1) begin
|
||||
if (rx_dv[i]) begin
|
||||
if (activity)
|
||||
jam = 1;
|
||||
else
|
||||
active_port = i;
|
||||
activity = 1;
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < PORT_COUNT; i = i + 1) begin
|
||||
tx_en_next[i] = 0;
|
||||
tx_er_next[i] = rx_er[active_port];
|
||||
txd_next[i] = rxd[active_port * 4 +: 4];
|
||||
if (jam) begin
|
||||
tx_en_next[i] = 1;
|
||||
tx_er_next[i] = 0;
|
||||
txd_next[i] = DATA_JAM;
|
||||
end else if (activity && i != active_port) begin
|
||||
tx_en_next[i] = 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
tx_en <= tx_en_next;
|
||||
tx_er <= tx_er_next;
|
||||
for (i = 0; i < PORT_COUNT; i = i + 1)
|
||||
txd[i * 4 +: 4] <= txd_next[i];
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,74 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-Only
|
||||
# Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
|
||||
|
||||
import itertools
|
||||
|
||||
import cocotb
|
||||
from cocotb.binary import BinaryValue
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.handle import ModifiableObject
|
||||
from cocotb.triggers import ClockCycles, Event, FallingEdge, RisingEdge, Timer
|
||||
from cocotb.types import LogicArray
|
||||
|
||||
PORT_COUNT = 4
|
||||
|
||||
class Memory(ModifiableObject):
|
||||
def __init__(self, handle, path):
|
||||
super().__init__(handle, path)
|
||||
|
||||
@cocotb.test(timeout_time=100, timeout_unit='ns')
|
||||
async def test_hub(hub):
|
||||
hub.rx_dv.value = 0
|
||||
await Timer(1)
|
||||
await cocotb.start(Clock(hub.clk, 8, units='ns').start())
|
||||
|
||||
await FallingEdge(hub.clk)
|
||||
assert not hub.tx_en.value
|
||||
|
||||
def check(jam, active_port=None, data=None):
|
||||
if jam:
|
||||
data = 5
|
||||
|
||||
for i in range(PORT_COUNT):
|
||||
if not jam and i == active_port:
|
||||
assert not hub.tx_en[i].value
|
||||
else:
|
||||
assert hub.tx_en[i].value
|
||||
if data is None:
|
||||
assert hub.tx_er[i].value
|
||||
else:
|
||||
assert not hub.tx_er[i].value
|
||||
assert hub.txd.value[i * 4:(i + 1) * 4 - 1] == data
|
||||
|
||||
hub.rx_dv[0].value = 1
|
||||
hub.rx_er[0].value = 0
|
||||
rxd = LogicArray(itertools.repeat('X', PORT_COUNT * 4))
|
||||
rxd[3:0] = BinaryValue(1, 4, False).binstr
|
||||
hub.rxd.value = rxd
|
||||
await FallingEdge(hub.clk)
|
||||
check(False, 0, 1)
|
||||
|
||||
hub.rx_er[0].value = 1
|
||||
await FallingEdge(hub.clk)
|
||||
check(False, 0)
|
||||
|
||||
hub.rx_dv[1].value = 1
|
||||
hub.rx_er[1].value = 0
|
||||
rxd[7:4] = BinaryValue(2, 4, False).binstr
|
||||
hub.rxd.value = rxd
|
||||
await FallingEdge(hub.clk)
|
||||
check(True)
|
||||
|
||||
hub.rx_dv[0].value = 0
|
||||
for i in range(1, PORT_COUNT):
|
||||
hub.rx_dv[i].value = 1
|
||||
hub.rx_er[i].value = 0
|
||||
rxd = LogicArray(itertools.repeat('X', PORT_COUNT * 4))
|
||||
rxd[(i + 1) * 4 - 1:i * 4] = BinaryValue(i + 1, 4, False).binstr
|
||||
hub.rxd.value = rxd
|
||||
await FallingEdge(hub.clk)
|
||||
check(False, i, i + 1)
|
||||
hub.rx_dv[i].value = 0
|
||||
|
||||
await FallingEdge(hub.clk)
|
||||
assert not hub.tx_en.value
|
Loading…
Reference in New Issue