Skip to content

Commit

Permalink
Add Intel Avalon MM cpuif. #40
Browse files Browse the repository at this point in the history
  • Loading branch information
amykyta3 committed May 15, 2023
1 parent b350da3 commit fadb8ce
Show file tree
Hide file tree
Showing 16 changed files with 408 additions and 43 deletions.
32 changes: 32 additions & 0 deletions docs/cpuif/avalon.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Intel Avalon
============

Implements the register block using an
`Intel Avalon MM <https://www.intel.com/content/www/us/en/docs/programmable/683091/22-3/memory-mapped-interfaces.html>`_
CPU interface.

The Avalon interface comes in two i/o port flavors:

SystemVerilog Interface
Class: :class:`peakrdl_regblock.cpuif.avalon.Avalon_Cpuif`

Interface Definition: :download:`avalon_mm_intf.sv <../../hdl-src/avalon_mm_intf.sv>`

Flattened inputs/outputs
Flattens the interface into discrete input and output ports.

Class: :class:`peakrdl_regblock.cpuif.avalon.Avalon_Cpuif_flattened`


Implementation Details
----------------------
This implementation of the Avalon protocol has the following features:

* Interface uses word addressing.
* Supports `pipelined transfers <https://www.intel.com/content/www/us/en/docs/programmable/683091/22-3/pipelined-transfers.html>`_
* Responses may have variable latency

In most cases, latency is fixed and is determined by how many retiming
stages are enabled in your design.
However if your design contains external components, access latency is
not guaranteed to be uniform.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ Links
cpuif/introduction
cpuif/apb
cpuif/axi4lite
cpuif/avalon
cpuif/passthrough
cpuif/internal_protocol
cpuif/customizing
Expand Down
46 changes: 46 additions & 0 deletions hdl-src/avalon_mm_intf.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
interface avalon_mm_intf #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32 // Important! Avalon uses word addressing
);
// Command
logic read;
logic write;
logic waitrequest;
logic [ADDR_WIDTH-1:0] address;
logic [DATA_WIDTH-1:0] writedata;
logic [DATA_WIDTH/8-1:0] byteenable;

// Response
logic readdatavalid;
logic writeresponsevalid;
logic [DATA_WIDTH-1:0] readdata;
logic [1:0] response;

modport host (
output read,
output write,
input waitrequest,
output address,
output writedata,
output byteenable,

input readdatavalid,
input writeresponsevalid,
input readdata,
input response
);

modport agent (
input read,
input write,
output waitrequest,
input address,
input writedata,
input byteenable,

output readdatavalid,
output writeresponsevalid,
output readdata,
output response
);
endinterface
6 changes: 4 additions & 2 deletions src/peakrdl_regblock/__peakrdl__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from peakrdl.config import schema #pylint: disable=import-error

from .exporter import RegblockExporter
from .cpuif import apb3, apb4, axi4lite, passthrough, CpuifBase
from .cpuif import CpuifBase, apb3, apb4, axi4lite, passthrough, avalon
from .udps import ALL_UDPS
from . import entry_points

Expand Down Expand Up @@ -48,13 +48,15 @@ def get_cpuifs(self) -> Dict[str, Type[CpuifBase]]:

# All built-in CPUIFs
cpuifs = {
"passthrough": passthrough.PassthroughCpuif,
"apb3": apb3.APB3_Cpuif,
"apb3-flat": apb3.APB3_Cpuif_flattened,
"apb4": apb4.APB4_Cpuif,
"apb4-flat": apb4.APB4_Cpuif_flattened,
"axi4-lite": axi4lite.AXI4Lite_Cpuif,
"axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened,
"passthrough": passthrough.PassthroughCpuif
"avalon-mm": avalon.Avalon_Cpuif,
"avalon-mm-flat": avalon.Avalon_Cpuif_flattened,
}

# Load any cpuifs specified via entry points
Expand Down
37 changes: 37 additions & 0 deletions src/peakrdl_regblock/cpuif/avalon/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from ..base import CpuifBase
from ...utils import clog2

class Avalon_Cpuif(CpuifBase):
template_path = "avalon_tmpl.sv"

@property
def port_declaration(self) -> str:
return "avalon_mm_intf.agent avalon"

def signal(self, name:str) -> str:
return "avalon." + name

@property
def word_addr_width(self) -> int:
# Avalon agents use word addressing, therefore address width is reduced
return self.addr_width - clog2(self.data_width_bytes)

class Avalon_Cpuif_flattened(Avalon_Cpuif):
@property
def port_declaration(self) -> str:
lines = [
"input wire " + self.signal("read"),
"input wire " + self.signal("write"),
"output logic " + self.signal("waitrequest"),
f"input wire [{self.word_addr_width-1}:0] " + self.signal("address"),
f"input wire [{self.data_width-1}:0] " + self.signal("writedata"),
f"input wire [{self.data_width_bytes-1}:0] " + self.signal("byteenable"),
"output logic " + self.signal("readdatavalid"),
"output logic " + self.signal("writeresponsevalid"),
f"output logic [{self.data_width-1}:0] " + self.signal("readdata"),
"output logic [1:0] " + self.signal("response"),
]
return ",\n".join(lines)

def signal(self, name:str) -> str:
return "avalon_" + name
29 changes: 29 additions & 0 deletions src/peakrdl_regblock/cpuif/avalon/avalon_tmpl.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Request
always_comb begin
cpuif_req = {{cpuif.signal("read")}} | {{cpuif.signal("write")}};
cpuif_req_is_wr = {{cpuif.signal("write")}};
{%- if cpuif.data_width_bytes == 1 %}
cpuif_addr = {{cpuif.signal("address")}};
{%- else %}
cpuif_addr = { {{-cpuif.signal("address")}}, {{clog2(cpuif.data_width_bytes)}}'b0};
{%- endif %}
cpuif_wr_data = {{cpuif.signal("writedata")}};
for(int i=0; i<{{cpuif.data_width_bytes}}; i++) begin
cpuif_wr_biten[i*8 +: 8] <= {8{ {{-cpuif.signal("byteenable")}}[i]}};
end
{{cpuif.signal("waitrequest")}} = (cpuif_req_stall_rd & {{cpuif.signal("read")}}) | (cpuif_req_stall_wr & {{cpuif.signal("write")}});
end

// Response
always_comb begin
{{cpuif.signal("readdatavalid")}} = cpuif_rd_ack;
{{cpuif.signal("writeresponsevalid")}} = cpuif_wr_ack;
{{cpuif.signal("readdata")}} = cpuif_rd_data;
if(cpuif_rd_err || cpuif_wr_err) begin
// SLVERR
{{cpuif.signal("response")}} = 2'b10;
end else begin
// OK
{{cpuif.signal("response")}} = 2'b00;
end
end
17 changes: 17 additions & 0 deletions tests/lib/cpuifs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from .passthrough import Passthrough
from .apb3 import APB3, FlatAPB3
from .apb4 import APB4, FlatAPB4
from .axi4lite import AXI4Lite, FlatAXI4Lite
from .avalon import Avalon, FlatAvalon

ALL_CPUIF = [
Passthrough(),
APB3(),
FlatAPB3(),
APB4(),
FlatAPB4(),
AXI4Lite(),
FlatAXI4Lite(),
Avalon(),
FlatAvalon(),
]
4 changes: 2 additions & 2 deletions tests/lib/cpuifs/apb4/apb4_intf_driver.sv
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ interface apb4_intf_driver #(

semaphore txn_mutex = new(1);

task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data);
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = '1);
txn_mutex.get();
##0;

Expand All @@ -69,7 +69,7 @@ interface apb4_intf_driver #(
cb.PPROT <= '0;
cb.PADDR <= addr;
cb.PWDATA <= data;
cb.PSTRB <= '1;
cb.PSTRB <= strb;
@(cb);

// active phase
Expand Down
18 changes: 18 additions & 0 deletions tests/lib/cpuifs/avalon/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from ..base import CpuifTestMode

from peakrdl_regblock.cpuif.avalon import Avalon_Cpuif, Avalon_Cpuif_flattened

class Avalon(CpuifTestMode):
cpuif_cls = Avalon_Cpuif
rtl_files = [
"../../../../hdl-src/avalon_mm_intf.sv",
]
tb_files = [
"../../../../hdl-src/avalon_mm_intf.sv",
"avalon_mm_intf_driver.sv",
]
tb_template = "tb_inst.sv"

class FlatAvalon(Avalon):
cpuif_cls = Avalon_Cpuif_flattened
rtl_files = []
138 changes: 138 additions & 0 deletions tests/lib/cpuifs/avalon/avalon_mm_intf_driver.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
interface avalon_mm_intf_driver #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst,
avalon_mm_intf.host avalon
);
localparam ADDR_PAD = $clog2(DATA_WIDTH/8);
localparam WORD_ADDR_WIDTH = ADDR_WIDTH - ADDR_PAD;

timeunit 1ps;
timeprecision 1ps;

logic av_read;
logic av_write;
logic av_waitrequest;
logic [WORD_ADDR_WIDTH-1:0] av_address;
logic [DATA_WIDTH-1:0] av_writedata;
logic [DATA_WIDTH/8-1:0] av_byteenable;
logic av_readdatavalid;
logic av_writeresponsevalid;
logic [DATA_WIDTH-1:0] av_readdata;
logic [1:0] av_response;

assign avalon.read = av_read;
assign avalon.write = av_write;
assign av_waitrequest = avalon.waitrequest;
assign avalon.address = av_address;
assign avalon.writedata = av_writedata;
assign avalon.byteenable = av_byteenable;
assign av_readdatavalid = avalon.readdatavalid;
assign av_writeresponsevalid = avalon.writeresponsevalid;
assign av_readdata = avalon.readdata;
assign av_response = avalon.response;

default clocking cb @(posedge clk);
default input #1step output #1;
output av_read;
output av_write;
input av_waitrequest;
output av_address;
output av_writedata;
output av_byteenable;
input av_readdatavalid;
input av_writeresponsevalid;
input av_readdata;
input av_response;
endclocking

task automatic reset();
cb.av_read <= '0;
cb.av_write <= '0;
cb.av_address <= '0;
cb.av_writedata <= '0;
cb.av_byteenable <= '0;
endtask

semaphore req_mutex = new(1);
semaphore resp_mutex = new(1);

task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = '1);
fork
begin
req_mutex.get();
##0;
// Initiate transfer
cb.av_write <= '1;
cb.av_address <= (addr >> ADDR_PAD);
cb.av_writedata <= data;
cb.av_byteenable <= strb;
@(cb);

// Wait for transfer to be accepted
while(cb.av_waitrequest == 1'b1) @(cb);
reset();
req_mutex.put();
end

begin
resp_mutex.get();
@cb;
// Wait for response
while(cb.av_writeresponsevalid !== 1'b1) @(cb);
assert(!$isunknown(cb.av_response)) else $error("Read from 0x%0x returned X's on av_response", addr);
resp_mutex.put();
end
join
endtask

task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
fork
begin
req_mutex.get();
##0;
// Initiate transfer
cb.av_read <= '1;
cb.av_address <= (addr >> ADDR_PAD);
@(cb);

// Wait for transfer to be accepted
while(cb.av_waitrequest == 1'b1) @(cb);
reset();
req_mutex.put();
end

begin
resp_mutex.get();
@cb;
// Wait for response
while(cb.av_readdatavalid !== 1'b1) @(cb);
assert(!$isunknown(cb.av_readdata)) else $error("Read from 0x%0x returned X's on av_response", av_readdata);
assert(!$isunknown(cb.av_response)) else $error("Read from 0x%0x returned X's on av_response", addr);
data = cb.av_readdata;
resp_mutex.put();
end
join
endtask

task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
logic [DATA_WIDTH-1:0] data;
read(addr, data);
data &= mask;
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
endtask

initial begin
reset();
end

initial forever begin
@cb;
if(!rst) assert(!$isunknown(cb.av_waitrequest)) else $error("Saw X on av_waitrequest!");
if(!rst) assert(!$isunknown(cb.av_readdatavalid)) else $error("Saw X on av_readdatavalid!");
if(!rst) assert(!$isunknown(cb.av_writeresponsevalid)) else $error("Saw X on av_writeresponsevalid!");
end

endinterface

0 comments on commit fadb8ce

Please sign in to comment.