Skip to content
Snippets Groups Projects
Commit 059d1661 authored by Daniel Newbrook's avatar Daniel Newbrook
Browse files

Initial Commit

parent 521868b9
No related branches found
No related tags found
No related merge requests found
./logical/*
\ No newline at end of file
makefile 0 → 100644
#-----------------------------------------------------------------------------
# NIC cocoTB Top-Level Makefile
# - Includes other Makefiles in flow directory
# A joint work commissioned on behalf of SoC Labs, under Arm Academic Access l
#
# Contributors
#
# David Flynn (d.w.flynn@soton.ac.uk)
# Daniel Newbrook (d.newbrook@soton.ac.uk)
# Copyright (C) 2021-3, SoC Labs (www.soclabs.org)
#-----------------------------------------------------------------------------
build_ip:
socrates_cli --project cocoTB_AXI -data ../ --flow build.configured.component configuredComponentName=nic400_1
socrates_cli --project cocoTB_AXI -data ../ --flow build.configured.component configuredComponentName=IntMemAxi_1
@$(ARM_IP_LIBRARY_PATH)/DMA-350/CG096-r0p0-00rel0/CG096-BU-50000-r0p0-00rel0/dma350/logical/generate --config ./socrates/dma350/config/cfg_dma_axi.yaml --output ./logical/dma350/
make_project:
socrates_cli --project cocoTB_AXI -data ../ --flow AddNewProject
all: make_project build_ip
clean:
@rm -rf ./logical/*
<?xml version="1.0" encoding="UTF-8"?>
<ConfiguredComponent version="r1p0">
<Name>IntMemAxi_1</Name>
<Description></Description>
<Suffix>1</Suffix>
<ConfigurationGroupName></ConfigurationGroupName>
<ConfigurableComponentRef>
<Vendor>arm.com</Vendor>
<Library>PrimeCell</Library>
<Name>IntMemAxi</Name>
<Version>r0p0_0</Version>
</ConfigurableComponentRef>
<Specification>
<Parameters>
<Parameter>
<Name>DATA_WIDTH</Name>
<Value>64</Value>
</Parameter>
<Parameter>
<Name>ID_WIDTH</Name>
<Value>5</Value>
</Parameter>
<Parameter>
<Name>NUM_RD_WS</Name>
<Value>0</Value>
</Parameter>
<Parameter>
<Name>IS_ROM</Name>
<Value>0</Value>
</Parameter>
</Parameters>
<Domains>
<VoltageDomains>
<VoltageDomain>
<Name>vd0</Name>
<UID>VD-vd0</UID>
</VoltageDomain>
</VoltageDomains>
<PowerDomains>
<PowerDomain>
<Name>pd0</Name>
<UID>PD-pd0</UID>
<Type>AlwaysOn</Type>
<VoltageDomainRef>VD-vd0</VoltageDomainRef>
</PowerDomain>
</PowerDomains>
<ClockDomains>
<ClockDomain>
<Name>ACLK</Name>
<UID>CD-ACLK</UID>
<PowerDomainRef>PD-pd0</PowerDomainRef>
</ClockDomain>
</ClockDomains>
</Domains>
<Interfaces>
<Interface>
<Name>SRAM_sp_basic_Master</Name>
<UID>IF-SRAM_sp_basic_Master</UID>
<Requester>
</Requester>
<Protocol>
<ProtocolRef>SRAM_sp_basic</ProtocolRef>
<Parameters>
<Parameter>
<Name>ADDR_WIDTH</Name>
<Value>6</Value>
</Parameter>
<Parameter>
<Name>DATA_WIDTH</Name>
<Value>64</Value>
</Parameter>
</Parameters>
</Protocol>
<ClockDomainRef>CD-ACLK</ClockDomainRef>
</Interface>
<Interface>
<Name>AXI_Slave</Name>
<UID>IF-AXI_Slave</UID>
<Completer>
</Completer>
<Protocol>
<ProtocolRef>AXI</ProtocolRef>
<Parameters>
<Parameter>
<Name>ADDR_WIDTH</Name>
<Value>32</Value>
</Parameter>
<Parameter>
<Name>DATA_WIDTH</Name>
<Value>1</Value>
</Parameter>
<Parameter>
<Name>ID_R_WIDTH</Name>
<Value>5</Value>
</Parameter>
<Parameter>
<Name>ID_W_WIDTH</Name>
<Value>5</Value>
</Parameter>
<Parameter>
<Name>AWUSER_WIDTH</Name>
<Value>0</Value>
</Parameter>
<Parameter>
<Name>ARUSER_WIDTH</Name>
<Value>0</Value>
</Parameter>
<Parameter>
<Name>WUSER_WIDTH</Name>
<Value>0</Value>
</Parameter>
<Parameter>
<Name>RUSER_WIDTH</Name>
<Value>0</Value>
</Parameter>
<Parameter>
<Name>BUSER_WIDTH</Name>
<Value>0</Value>
</Parameter>
<Parameter>
<Name>LockSupport</Name>
<Value>false()</Value>
</Parameter>
</Parameters>
</Protocol>
<ClockDomainRef>CD-ACLK</ClockDomainRef>
</Interface>
<Interface>
<Name>DFTInterface_Slave</Name>
<UID>IF-DFTInterface_Slave</UID>
<Completer>
</Completer>
<Protocol>
<ProtocolRef>DFTInterface</ProtocolRef>
</Protocol>
</Interface>
</Interfaces>
</Specification>
</ConfiguredComponent>
\ No newline at end of file
This diff is collapsed.
//----------------------------------------------------------------------------
// The confidential and proprietary information contained in this file may
// only be used by a person authorised under and to the extent permitted
// by a subsisting licensing agreement from Arm Limited or its affiliates.
//
// (C) COPYRIGHT 2021-2022 Arm Limited or its affiliates.
// ALL RIGHTS RESERVED
//
// This entire notice must be reproduced on all copies of this file
// and copies of this file may only be made by a person if such person is
// permitted to do so under the terms of a subsisting license agreement
// from Arm Limited or its affiliates.
//
// Release Information : DMA350-r0p0-00rel0
//
//----------------------------------------------------------------------------
function automatic bit address_map_m1 (
input [<<ADDR_WIDTH>>-1:0] axaddr,
input [2:0] axprot
);
bit res;
begin
res = '1;
if (axaddr[31:20] == 12'h000 || axaddr[31:20] == 12'h200) begin
res = '0;
end
return res;
end
endfunction
#----------------------------------------------------------------------------
# The confidential and proprietary information contained in this file may
# only be used by a person authorised under and to the extent permitted
# by a subsisting licensing agreement from Arm Limited or its affiliates.
#
# (C) COPYRIGHT 2021-2022 Arm Limited or its affiliates.
# ALL RIGHTS RESERVED
#
# This entire notice must be reproduced on all copies of this file
# and copies of this file may only be made by a person if such person is
# permitted to do so under the terms of a subsisting license agreement
# from Arm Limited or its affiliates.
#----------------------------------------------------------------------------
#
# Release Information : DMA350-r0p0-00rel0
#
# -----------------------------------------------------------------------------
# Abstract : User Configuration file for ADA DMA
# -----------------------------------------------------------------------------
#
# CONFIG_NAME: Name of the configuration.
# Each unifiqued element and top is suffixed with
# _${CONFIG_NAME}
#
CONFIG_NAME: sldma350
#
# ADDR_WIDTH: Address Bus width
#
# Valid values:
# 32-64
ADDR_WIDTH: 32
#
# DATA_WIDTH: Data Bus width
#
# Valid values:
# [32,64,128]
DATA_WIDTH: 64
#
# CHID_WIDTH: Width of the configurable channel ID user signal.
# When set to 0, then the archid and awchid ports are not present on the module.
#
# Valid values:
# 0-16
CHID_WIDTH: 4
#
# GPO_WIDTH: Width of GPO output for every channel. When multiple channels have GPOs
# then the width must be set to the maximum number of GPOs a channel can have,
# and unused GPO ports need to be left unconnected. When all bits of CH_GPO_MASK
# is 0, this parameter is not relevant.
#
# Valid values:
# 1-32
GPO_WIDTH: 1
#
# CH_GPO_MASK: A bitmask for enabling the GPO port for each channel. The width of the
# bitmask is NUM_CHANNELS-1. When bit n is set to 1 then the GPO is enabled for
# channel n and the gpo_ch_n[GPO_WIDTH-1:0] port appears on the module.
#
# Valid values:
# 0-(2^NUM_CHANNELS-1)
CH_GPO_MASK: 0x0
#
# CH_STREAM_MASK: A bitmask for enabling the stream interfaces for each channel.
# The width of the bitmask is NUM_CHANNELS-1. When bit n is set to 1 then
# the stream interfaces are enabled for channel n and the relevant ports
# appears on the module. NOTE: When streaming interface is enabled the actual
# FIFO size of the channel will be the double of CH_<N>_FIFO_DEPTH
#
# Valid values:
# 0-(2^NUM_CHANNELS-1)
CH_STREAM_MASK: 0x1
#
# CH_<N>_FIFO_DEPTH: Sets the FIFO depth for channel <N> that defines the number of
# DATA_WIDTH size entries a channel can hold for a transfer. N goes from 0 to
# NUM_CHANNELS-1. In combination with the TRANSIZE setting of the command, the
# FIFO depth defines the maximum burst size a channel can support. This setting
# needs to be aligned with the bandwidth requirements of the channel but it
# highly affects the area of the design.
#
# Valid values:
# [1,2,4,8,16,32,64]
CH_0_FIFO_DEPTH: 2
#
# CH_EXT_FEAT_MASK: A bitmask for enabling the extended feature set for each channel.
# The extension contains 2D, WRAP, TMPLT features. Default value enables it for
# the number of channels.
#
# Valid values:
# 0-(2^NUM_CHANNELS-1)
CH_EXT_FEAT_MASK: 0x0
#
# NUM_CHANNELS: Number of configurable DMA channels.
#
# Valid values:
# 1-8
NUM_CHANNELS: 1
#
# NUM_TRIGGER_IN: Number of trigger input ports.
#
# Valid values:
# 0-32
NUM_TRIGGER_IN: 1
#
# NUM_TRIGGER_OUT: Number of trigger output ports.
#
# Valid values:
# 0-32
NUM_TRIGGER_OUT: 1
#
# TRIG_IN_SYNC_EN_MASK: A bitmask for enabling the synchronizers on the trigger in
# interfaces for each trigger port. The width of the bitmask is NUM_TRIGGER_IN-1.
# When bit n is set to 1 then the trigger in interface is considered asynchronous
# and the synchronizer logic is placed on the selected input ports.
#
# Valid values:
# 0-(2^NUM_TRIGGER_IN-1)
TRIG_IN_SYNC_EN_MASK: 0x0
#
# TRIG_OUT_SYNC_EN_MASK: A bitmask for enabling the synchronizers on the trigger out
# interfaces for each trigger port. The width of the bitmask is NUM_TRIGGER_OUT-1.
# When bit n is set to 1 then the trigger out interface is considered asynchronous
# and the synchronizer logic is placed on the selected input ports.
#
# Valid values:
# 0-(2^NUM_TRIGGER_OUT-1)
TRIG_OUT_SYNC_EN_MASK: 0x0
#
# AXI5_M1_PRESENT: Enables an additional master port. When set the m1 master port is
# present on the top level port list and additional include file can be used with
# a System Verilog function that defines which address ranges are mapped to the m1
# interface.
#
# Valid values:
# [0,1]
AXI5_M1_PRESENT: 1
#
# SECEXT_PRESENT: Enables TrustZone security support.
#
# Valid values:
# [0,1]
SECEXT_PRESENT: 0
#
# AXI5_M1_ADDR_MAP: Select AXI M1 master.
#
# Valid values:
# relative path to logical
AXI5_M1_ADDR_MAP: models/modules/generic/address_map_m1_nanosoc.sv
This diff is collapsed.
# Copyright (c) 2020 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 ?= questa
WAVES ?= 0
COCOTB_HDL_TIMEUNIT = 1ns
COCOTB_HDL_TIMEPRECISION = 1ns
DUT = nic400_top
TOPLEVEL = nic400_top
MODULE = test_axi
VERILOG_SOURCES += ./nic400_top.v
ifeq ($(SIM), icarus)
PLUSARGS += -fst
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1)
VERILOG_SOURCES += iverilog_dump.v
COMPILE_ARGS += -s iverilog_dump
endif
else ifeq ($(SIM), verilator)
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH -Wno-CASEINCOMPLETE
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
ifeq ($(WAVES), 1)
COMPILE_ARGS += --trace-fst
endif
endif
include ./makefile.flist
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
# MIT License
# Copyright (c) 2021 SystematIC Design BV
# 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.
"""
APB Transaction and Agent
(Driver + Monitor)
"""
from collections import deque
import random
import cocotb
from cocotb.triggers import RisingEdge, ReadOnly
from cocotb.binary import BinaryValue
from cocotb_bus.drivers import BusDriver
from cocotb_bus.monitors import BusMonitor
from cocotb.result import ReturnValue
from cocotb.decorators import coroutine
from cocotb_coverage.crv import Randomized
# define the PWRITE mapping
pwrite = [ 'READ',
'WRITE' ]
class APBTransaction(Randomized):
"""
APB Transaction Class
Defines the transaction in terms of the fields
"""
def __init__(self, address, data=None, direction=None, strobe=[True,True,True,True],
error=None, bus_width=32, address_width=12):
Randomized.__init__(self)
# check input values
assert direction in [None, 'READ', 'WRITE'], "The direction must be either: None, 'READ', 'WRITE'"
# select based on read/write operation
if data != None:
if direction:
self.direction = direction
else:
self.direction = 'WRITE'
self.data = data
else:
self.direction = 'READ'
self.data = None
# save the straight through parameters
self.address = address
self.bus_width = bus_width
self.address_width = address_width
self.strobe = strobe
# store the error setting
if error != None:
self.error = error
else:
self.error = False
# store time of the transaction
self.start_time = None
def post_randomize(self):
'''
Generate a randomized transaction
'''
# select a random direction
self.direction = ['READ','WRITE'][random.randint(0,1)]
# select the transaction length
self.address = random.randint(0,2**(self.address_width-2))*4
# if we're writing generate the data
if self.direction == 'WRITE':
self.data = random.randint(0,self.bus_width)
# create random strobe data
for i in range(4):
self.strobe[i] = bool(random.randint(0,1))
def print(self):
'''
Print a transaction information in a nice readable format
'''
print('-'*120)
print('APB Transaction - ', end='')
if self.start_time:
print('Started at %d ns' % self.start_time)
else:
print('Has not occurred yet')
print('')
print(' Address: 0x%08X' % self.address)
print(' Direction: %s' % self.direction)
print(' Data: ', end='')
if self.data != None:
print('0x%0*X ' % (int(self.bus_width/4),self.data))
else:
print('NO DATA YET!')
if self.error:
print(' TRANSACTION ENDED IN ERROR!')
print('')
print('-'*120)
def convert2string(self):
"""
Returns a string - used by UVM.
"""
return "APB: address: %s, direction: %s, data: %s, strobe: %s" % (
hex(self.address), self.direction, hex(self.data), hex(self._strobe()) )
#overload (not)equlity operators - just compare mosi and miso data match
def __ne__(self, other):
return NotImplemented
def __eq__(self, other):
# compare each field
fail = False
fail = fail or not (self.address == other.address)
fail = fail or not (self.direction == other.direction)
fail = fail or not (self.data == other.data)
# return response
return not fail
def _strobe(self):
"""
Return an integer representation of the byte strobes.
"""
try:
return int(''.join([ '1' if x else '0' for x in self.strobe ]), 2)
except ValueError as e:
print(self.strobe)
raise e
def __repr__(self):
return self.convert2string()
class APBMonitor(BusMonitor):
"""
APB Master Monitor
Observes the bust to monitor all transactions and provide callbacks
with the observed data
"""
def __init__(self, entity, name, clock, pkg=False, signals=None, bus_width=32, **kwargs):
# has the signals been explicitely defined?
if signals:
self._signals = signals
else:
# a SystemVerilog package is used
if pkg:
self._signals = {}
for signal_name in ['psel', 'pwrite', 'penable', 'paddr', 'pwdata', 'pstrb']:
self._signals[signal_name.upper()] = name + '_h2d_i.' + signal_name
for signal_name in ['prdata', 'pready', 'pslverr']:
self._signals[signal_name.upper()] = name + '_d2h_o.' + signal_name
name = None
# just use the default APB names
else:
self._signals = [
"PSEL",
"PWRITE",
"PENABLE",
"PADDR",
"PWDATA",
"PRDATA",
"PREADY"]
self._optional_signals = [
"PSLVERR",
"PSTRB"]
BusMonitor.__init__(self, entity, name, clock, **kwargs)
self.clock = clock
self.bus_width = bus_width
# prime the monitor to begin
self.reset()
def reset(self):
'''
Mimic the reset functon in hardware
'''
pass
async def _monitor_recv(self):
'''
Keep watching the bus until the peripheral is signalled as:
Selected
Enabled
Ready
Then simply sample the address, data and direction
'''
await RisingEdge(self.clock)
while True:
# both slave and master are ready for transfer
if self.bus.PSEL.value.integer and self.bus.PENABLE.value.integer and self.bus.PREADY.value.integer:
# retrieve the data from the bus
address = self.bus.PADDR.value.integer
direction = pwrite[self.bus.PWRITE.value.integer]
# are we reading or writing?
if direction == 'READ':
data = self.bus.PRDATA.value.integer
else:
data = self.bus.PWDATA.value.integer
# store the transaction object
transaction = APBTransaction( address = address,
data = data,
direction = direction)
transaction.start_time = cocotb.utils.get_sim_time('ns')
# find out if there's an error from the slave
if self.bus.PSLVERR.value.integer:
transaction.error = True
# signal to the callback
self._recv(transaction)
# begin next cycle
await RisingEdge(self.clock)
class APBMasterDriver(BusDriver):
"""
APB Master Driver
Drives data onto the APB bus to setup for read/write to slave devices.
"""
def __init__(self, entity, name, clock, pkg=False, signals=None, **kwargs):
# has the signals been explicitely defined?
if signals:
self._signals = signals
else:
# a SystemVerilog package is used
if pkg:
self._signals = {}
for signal_name in ['psel', 'pwrite', 'penable', 'paddr', 'pwdata', 'pstrb']:
self._signals[signal_name.upper()] = name + '_h2d_i.' + signal_name
for signal_name in ['prdata', 'pready', 'pslverr']:
self._signals[signal_name.upper()] = name + '_d2h_o.' + signal_name
name = None
# just use the default APB names
else:
self._signals = [
"PSEL",
"PWRITE",
"PENABLE",
"PADDR",
"PWDATA",
"PRDATA",
"PREADY"]
self._optional_signals = [
"PSLVERR",
"PSTRB"]
# inheret the bus driver
BusDriver.__init__(self, entity, name, clock, bus_separator='_', **kwargs)
self.clock = clock
# initialise all outputs to zero
self.bus.PADDR.setimmediatevalue(0)
self.bus.PWRITE.setimmediatevalue(0)
self.bus.PSEL.setimmediatevalue(0)
self.bus.PENABLE.setimmediatevalue(0)
self.bus.PWDATA.setimmediatevalue(0)
self.bus.PSTRB.setimmediatevalue(0)
self.reset()
def reset(self):
'''
Mimic the reset function in hardware
'''
# initialise the transmit queue
self.transmit_queue = deque()
self.transmit_coroutine = 0
@coroutine
async def busy_send(self, transaction):
'''
Provide a send method that waits for the transaction to complete.
'''
await self.send(transaction)
while (self.transfer_busy):
await RisingEdge(self.clock)
@coroutine
async def _driver_send(self, transaction, sync=True, hold=False, **kwargs):
'''
Append a new transaction to be transmitted
'''
# add new transaction
self.transmit_queue.append(transaction)
# launch new transmit pipeline coroutine if aren't holding for and the
# the coroutine isn't already running.
# If it is running it will just collect the transactions in the
# queue once it gets to them.
if not hold:
if not self.transmit_coroutine:
self.transmit_coroutine = cocotb.fork(self._transmit_pipeline())
@coroutine
async def _transmit_pipeline(self):
'''
Maintain a parallel operation transmitting all the items
in the pipline
'''
# default values
transaction_remaining = 0
state = 'SETUP'
self.transfer_busy = True
# while there's data in the queue keep transmitting
while len(self.transmit_queue) > 0 or state != 'IDLE':
if state == 'SETUP':
# get a new transaction from the queue
current_transaction = self.transmit_queue.popleft()
current_transaction.start_time = cocotb.utils.get_sim_time('ns')
# assign values in the control phase
self.bus.PSEL <= 1
self.bus.PADDR <= current_transaction.address
self.bus.PWRITE <= pwrite.index(current_transaction.direction)
# create the PSTRB signal
pstrb_int = 0
for i, pstrb_i in enumerate(current_transaction.strobe):
pstrb_int += pstrb_i << i
self.bus.PSTRB <= pstrb_int
# write the data to the bus
if current_transaction.direction == 'WRITE':
self.bus.PWDATA <= current_transaction.data
# update state
state = 'ACCESS'
elif state == 'ACCESS':
# tell the slave we're ready for the access phase
self.bus.PENABLE <= 1
state = 'SAMPLE'
await RisingEdge(self.clock)
if state == 'SAMPLE':
# is the slave ready?
if self.bus.PREADY.value.integer:
# check if the slave is asserting an error
if self.bus.PSLVERR.value.integer:
current_transaction.error = True
# if this is a read we should sample the data
if current_transaction.direction == 'READ':
current_transaction.data = self.bus.PRDATA.value.integer
# what's the next state?
if len(self.transmit_queue) > 0:
state = 'SETUP'
else:
state = 'IDLE'
self.bus.PENABLE <= 0
# reset the bus signals
self.bus.PWDATA <= 0
self.bus.PWRITE <= 0
self.bus.PSEL <= 0
self.bus.PENABLE <= 0
self.transfer_busy = False
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<testsuites name="results">
<testsuite name="all" package="all">
<property name="random_seed" value="1692283650" />
<testcase classname="test_axi" file="/home/dwn1c21/SoC-Labs/NIC_cocoTB/verif/test_axi.py" lineno="117" name="run_dma_1D_test" ratio_time="4853.842475586658" sim_time_ns="8926.001" time="1.8389556407928467" />
</testsuite>
</testsuites>
"""
Copyright (c) 2020 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 os
from numpy 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 AxiBus, AxiMaster, AxiBurstType
import apb
class TB:
def __init__(self, dut):
self.dut = dut
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())
self.apb_master = apb.APBMasterDriver(dut, "APB", dut.clk)
print(AxiBus.from_prefix(dut, "axi"))
print(AxiBus.from_prefix(dut, "axi").write)
self.axi_master = AxiMaster(AxiBus.from_prefix(dut, "axi"), dut.clk, dut.rst, reset_active_level=False)
self.log.info(self.apb_master)
def set_idle_generator(self, generator=None):
if generator:
self.axi_master.write_if.aw_channel.set_pause_generator(generator())
self.axi_master.write_if.w_channel.set_pause_generator(generator())
self.axi_master.read_if.ar_channel.set_pause_generator(generator())
def set_backpressure_generator(self, generator=None):
if generator:
self.axi_master.write_if.b_channel.set_pause_generator(generator())
self.axi_master.read_if.r_channel.set_pause_generator(generator())
async def cycle_reset(self):
self.dut.rst.setimmediatevalue(1)
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst.value = 0
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst.value = 1
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
async def delay(self, cycle):
for i in range(cycle):
await RisingEdge(self.dut.clk)
async def write_read(dut, tb, base_addr, byte_lanes, size):
for length in list(range(1, byte_lanes*2))+[1024]:
for offset in list(range(byte_lanes))+list(range(2048-byte_lanes, 2048)):
tb.log.info("length %d, offset %d", length, offset)
addr = offset+base_addr
test_data = bytearray([x % 256 for x in range(length)])
await tb.axi_master.write(addr, test_data, size=size)
data = await tb.axi_master.read(addr, length, size=size)
assert data.data == test_data
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
@cocotb.test()
async def run_test_write_read(dut, idle_inserter=None, backpressure_inserter=None, size=None):
tb = TB(dut)
byte_lanes = tb.axi_master.write_if.byte_lanes
max_burst_size = tb.axi_master.write_if.max_burst_size
if size is None:
size = max_burst_size
await tb.cycle_reset()
tb.set_idle_generator(idle_inserter)
tb.set_backpressure_generator(backpressure_inserter)
await write_read(dut, tb, 0x00000000, byte_lanes, size)
await write_read(dut, tb, 0x08000000, byte_lanes, size)
await write_read(dut, tb, 0x10000000, byte_lanes, size)
await write_read(dut, tb, 0x18000000, byte_lanes, size)
@cocotb.test()
async def run_dma_1D_test(dut,idle_inserter=None, backpressure_inserter=None, size=None):
tb = TB(dut)
src_addr = 0x00000000
dest_addr = 0x18000000
transfer_size = 0x00FF
max_burst_size = tb.axi_master.write_if.max_burst_size
if size is None:
size = max_burst_size
await tb.cycle_reset()
# Write random data to source address
for offset in range(0, transfer_size*8, 8):
addr = src_addr + offset
test_data = random.bytes(8)
await tb.axi_master.write(addr, test_data, size=size)
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
tb.log.info("Start DMA setup")
await tb.apb_master.busy_send(apb.APBTransaction(0x1010,0x00000000)) #Set source Address
await tb.apb_master.busy_send(apb.APBTransaction(0x1018,0x18000000)) #Set Destination address
await tb.apb_master.busy_send(apb.APBTransaction(0x1020,0x00FF00FF)) #Set Source and destination size
await tb.apb_master.busy_send(apb.APBTransaction(0x100C,0x000F02F3)) #Config bits 0 001 111 0 000 001 0 1111 0 011
await tb.apb_master.busy_send(apb.APBTransaction(0x102C,0x000F0444)) #Dest trans config
await tb.apb_master.busy_send(apb.APBTransaction(0x1028,0x000F0444))
await tb.apb_master.busy_send(apb.APBTransaction(0x1030,0x00010001))
tb.log.info("Finish DMA setup")
await tb.apb_master.busy_send(apb.APBTransaction(0x1000,0x00000001))
tstart=cocotb.utils.get_sim_time('ns')
await tb.delay(20)
j=0
while dut.dma_active==1:
await tb.delay(100)
j+=1
if j>20:
break
status = apb.APBTransaction(0x1004)
await tb.apb_master.busy_send(status)
tb.log.info(status)
tb.log.info(status.data)
tend = cocotb.utils.get_sim_time('ns')
for offset in range(0, transfer_size*8, 8):
src_data = await tb.axi_master.read(src_addr+offset, 8, size=size)
dest_data = await tb.axi_master.read(dest_addr+offset, 8, size=size)
assert src_data.data == dest_data.data
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
n_cycles = (tend-tstart)/(2*255)
tb.log.info("Average no of clocks per transaction = " + str(n_cycles))
bandwidth = 64*255/(tend-tstart)
tb.log.info("Bandwidth of DMA transfer = " + str(bandwidth) + " Gbps")
@cocotb.test()
async def run_dma_1D_axis_test(dut,idle_inserter=None, backpressure_inserter=None, size=None):
tb = TB(dut)
src_addr = 0x00000100
dest_addr = 0x18000100
transfer_size = 0x00FF
max_burst_size = tb.axi_master.write_if.max_burst_size
if size is None:
size = max_burst_size
await tb.cycle_reset()
# Write random data to source address
for offset in range(0, transfer_size*8, 8):
addr = src_addr + offset
test_data = random.bytes(8)
await tb.axi_master.write(addr, test_data, size=size)
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
tb.log.info("Start DMA setup")
await tb.apb_master.busy_send(apb.APBTransaction(0x1010,0x00000100)) #Set source Address
await tb.apb_master.busy_send(apb.APBTransaction(0x1018,0x18000100)) #Set Destination address
await tb.apb_master.busy_send(apb.APBTransaction(0x1020,0x00FF00FF)) #Set Source and destination size
await tb.apb_master.busy_send(apb.APBTransaction(0x100C,0x200F02F3)) #Config bits 0010 1000 0000 1111 0000 0010 1111 0011
await tb.apb_master.busy_send(apb.APBTransaction(0x102C,0x000F0444)) #Dest trans config
await tb.apb_master.busy_send(apb.APBTransaction(0x1028,0x000F0444))
await tb.apb_master.busy_send(apb.APBTransaction(0x1030,0x00010001))
await tb.apb_master.busy_send(apb.APBTransaction(0x1068,0x00000000))
tb.log.info("Finish DMA setup")
await tb.apb_master.busy_send(apb.APBTransaction(0x1000,0x00000001))
tstart=cocotb.utils.get_sim_time('ns')
await tb.delay(20)
j=0
while dut.dma_active==1:
await tb.delay(100)
j+=1
if j>20:
break
status = apb.APBTransaction(0x1004)
await tb.apb_master.busy_send(status)
tb.log.info(status)
tb.log.info(status.data)
tend = cocotb.utils.get_sim_time('ns')
for offset in range(0, transfer_size*8, 8):
src_data = await tb.axi_master.read(src_addr+offset, 8, size=size)
dest_data = await tb.axi_master.read(dest_addr+offset, 8, size=size)
assert src_data.data == dest_data.data
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
n_cycles = (tend-tstart)/(2*255)
tb.log.info("Average no of clocks per transaction = " + str(n_cycles))
bandwidth = 64*255/(tend-tstart)
tb.log.info("Bandwidth of DMA transfer = " + str(bandwidth) + " Gbps")
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment