Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions ethernet_parser/rtl/ethernet_parser.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import parser_pkg::*;

module ethernet_parser #(
parameter DATA_IN_W = 64,
parameter BYTES_OUT = DATA_IN_W / SIZE_BYTE
)(
input logic clk,
input logic rst,
input logic data_valid_i,
input logic [DATA_IN_W-1:0] data_i,
input logic [BYTES_OUT-1:0] bytes_valid_i,

output logic sof_type_o, //1 = sof0 0 = sof4
output logic payload_time_o, //high when payload
output logic valid_o,

output outputs_t outputs_o //theres got to be a way to make one enum outport, idk if its it
);

typedef enum logic [3:0] {IDLE, PAUSE, PARSE_L4, PARSE_L0} state_t;

outputs_t outputs_d;
logic [$bits(outputs_t)-1:0] outputs_pipe_i;
logic [$bits(outputs_t)-1:0] outputs_pipe_o;

state_t current_state, next_state;

logic valid_d;
logic sof_or_eof_d, sof_or_eof_q; //0 = this is a start, 1 = this is an eof
logic sof_type_d;
logic payload_time_d;

assign outputs_pipe_i = outputs_d;
assign outputs_o = outputs_t'(outputs_pipe_o);

always_comb begin
//defaults
payload_time_d = '0;
valid_d = '0;
sof_type_d = '0;
outputs_d = OTHER;
sof_or_eof_d = sof_or_eof_q;
next_state = current_state;

case(current_state)
IDLE : begin
if(data_valid_i) begin
if(sof_or_eof_q) begin
if(bytes_valid_i == 8'hE0) begin
sof_or_eof_d = 1'b0;
end else if(bytes_valid_i != 8'hFF) begin
sof_or_eof_d = 1'b0;
end
end else if(bytes_valid_i == 8'hFE) begin //b1111_1110
sof_or_eof_d = 1'b1;
next_state = PARSE_L0;
payload_time_d = 1'b1;
sof_type_d = 1'b1;
end else if(bytes_valid_i == 8'hE0) begin //b1110_0000
sof_or_eof_d = 1'b1;
next_state = PAUSE;
end
end
end

PAUSE : begin
if(data_valid_i) begin
next_state = PARSE_L4;
payload_time_d = 1'b1;
end
end

PARSE_L0 : begin
if(data_valid_i) begin
next_state = IDLE;
valid_d = 1'b1;
if (data_i[55-:SIZE_BYTE*2] == IPV4_CODE) begin
outputs_d = IPV4;
end else if (data_i[55-:SIZE_BYTE*2] == IPV6_CODE) begin
outputs_d = IPV6;
end
end
end

PARSE_L4 : begin
if(data_valid_i) begin
next_state = IDLE;
valid_d = 1'b1;
if (data_i[23-:SIZE_BYTE*2] == IPV4_CODE) begin
outputs_d = IPV4;
end else if (data_i[23-:SIZE_BYTE*2] == IPV6_CODE) begin
outputs_d = IPV6;
end
end
end
endcase
end

always_ff@(posedge clk)begin
if(rst) begin
current_state = IDLE;
sof_or_eof_q = '0;
end else begin
current_state = next_state;
sof_or_eof_q = sof_or_eof_d;
end
end

data_pipeline #(
.DATA_W(1),
.PIPE_DEPTH(1),
.RST_EN(1),
.RST_VAL(0)
) pipeline_1 (
.clk(clk),
.rst(rst),
.data_i(valid_d),
.data_o(valid_o)
);

data_pipeline #(
.DATA_W(1),
.PIPE_DEPTH(1),
.RST_EN(1),
.RST_VAL(0)
) pipeline_2 (
.clk(clk),
.rst(rst),
.data_i(payload_time_d),
.data_o(payload_time_o)
);

data_pipeline #(
.DATA_W($bits(outputs_t)),
.PIPE_DEPTH(1),
.RST_EN(0)
) pipeline_3 (
.clk(clk),
.rst(rst),
.data_i(outputs_pipe_i),
.data_o(outputs_pipe_o)
);

data_pipeline #(
.DATA_W(1),
.PIPE_DEPTH(1),
.RST_EN(0)
) pipeline_4 (
.clk(clk),
.rst(rst),
.data_i(sof_type_d),
.data_o(sof_type_o)
);

endmodule : ethernet_parser
9 changes: 9 additions & 0 deletions ethernet_parser/rtl/parser_pkg.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package parser_pkg;

localparam IPV4_CODE = 16'h0008;
localparam IPV6_CODE = 16'hDD86;

typedef enum logic [2:0] {IPV4, IPV6, OTHER} outputs_t;
localparam SIZE_BYTE = 8;

endpackage
32 changes: 32 additions & 0 deletions ethernet_parser/tb/ethernet_parser_driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from dataclasses import fields, is_dataclass
from typing import TypeVar

from tb_utils.abstract_transactions import AbstractTransaction
from tb_utils.generic_drivers import GenericDriver

GenericSequenceItem = TypeVar("GenericSequenceItem", bound=AbstractTransaction)


class EthernetParserDriver(GenericDriver[GenericSequenceItem]):
def __init__(self, dut, seq_item_type: GenericSequenceItem):
super().__init__(dut, seq_item_type, clk=dut.clk)

async def recursive_drive(self, input_parent, item):
for f in fields(item):
if f.metadata.get("model_only", False):
continue

field_name = f.name
value = getattr(item, field_name)

if hasattr(input_parent, field_name):
signal_or_interface = getattr(input_parent, field_name)
if is_dataclass(value):
await self.recursive_drive(signal_or_interface, value)
else:
signal_or_interface.value = value
else:
raise AttributeError(
f"Field '{field_name}' found in sequence item "
f"but NOT in DUT handle '{input_parent._name}'."
)
67 changes: 67 additions & 0 deletions ethernet_parser/tb/ethernet_parser_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from typing import Any, Dict

from tb_utils.generic_model import GenericModel


class EthernetParserModel(GenericModel):
IDLE = "IDLE"
PAUSE = "PAUSE"
PARSE_L0 = "PARSE_L0"
PARSE_L4 = "PARSE_L4"

SOF0_MASK = 0xFE
SOF4_MASK = 0xE0
FULL_MASK = 0xFF
OUTPUT_OTHER = 2

def __init__(self):
super().__init__()
self.state = self.IDLE
self.inside_frame = False

@staticmethod
def _to_int(value: Any, default: int = 0) -> int:
if value is None:
return default
return int(value)

@staticmethod
def _to_bool(value: Any) -> bool:
return bool(value)

async def process_notification(self, notification: Dict[str, Any]):
data_valid = self._to_bool(notification.get("data_valid", False))
bytes_valid = self._to_int(notification.get("bytes_valid"), 0)
expected_payload_time = self._to_int(
notification.get("expected_payload_time_o"), 0
)
expected_output = self._to_int(
notification.get("expected_outputs_o"), self.OUTPUT_OTHER
)

if self.state == self.IDLE:
if data_valid and self.inside_frame:
if bytes_valid != self.FULL_MASK:
self.inside_frame = False
elif data_valid and bytes_valid == self.SOF0_MASK:
self.inside_frame = True
self.state = self.PARSE_L0
elif data_valid and bytes_valid == self.SOF4_MASK:
self.inside_frame = True
self.state = self.PAUSE
return

if self.state == self.PAUSE:
if data_valid:
self.state = self.PARSE_L4
return

if self.state in (self.PARSE_L0, self.PARSE_L4):
if data_valid:
await self.expected_queue.put(
{
"outputs_o": expected_output,
"payload_time_o": expected_payload_time,
}
)
self.state = self.IDLE
44 changes: 44 additions & 0 deletions ethernet_parser/tb/ethernet_parser_monitor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from typing import Any, Type

from cocotb.triggers import ReadOnly, RisingEdge
from cocotb.types import Logic

from ethernet_parser.tb.ethernet_parser_transaction import EthernetParserTransaction
from tb_utils.generic_monitor import GenericMonitor


class EthernetParserMonitor(GenericMonitor[EthernetParserTransaction]):
def __init__(
self,
dut,
output_transaction: Type[EthernetParserTransaction],
clk=None,
):
self._previous_payload_time_o = 0
super().__init__(dut=dut, output_transaction=output_transaction, clk=clk)

@staticmethod
def _to_int(value: Any, default: int = 0) -> int:
try:
return int(value)
except (TypeError, ValueError):
return default

async def receive_transaction(self) -> EthernetParserTransaction:
while True:
await RisingEdge(self._clk)
await ReadOnly()

current_payload_time = self._to_int(self.dut.payload_time_o.value, 0)
output_transaction = self.output_transaction()

if self._to_int(self.dut.valid_o.value, 0):
output_transaction.valid_o = Logic(1)
output_transaction.outputs_o = self.dut.outputs_o.value
output_transaction.payload_time_o = Logic(
self._previous_payload_time_o
)
self._previous_payload_time_o = current_payload_time
return output_transaction

self._previous_payload_time_o = current_payload_time
Loading
Loading