Skip to content

Commit

Permalink
iDMA: Copy sources from various repos, creating a dedicated iDMA repo
Browse files Browse the repository at this point in the history
  • Loading branch information
thommythomaso committed Jul 15, 2022
0 parents commit a2abe20
Show file tree
Hide file tree
Showing 41 changed files with 6,776 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.bender
work
scripts/compile.tcl
scripts/__pycache__
*.wlf
dma_trace*.log
dma_trace*.txt
dma_transfers*
modelsim.ini
transcript
31 changes: 31 additions & 0 deletions Bender.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
packages:
axi:
revision: 442ff3375710513623f95944d66cc2bd09b2f155
version: 0.29.1
source:
Git: "https://github.com/pulp-platform/axi.git"
dependencies:
- common_cells
- common_verification
common_cells:
revision: 015917ff33e5f944e866814f72f2074fb0f4220f
version: 1.22.1
source:
Git: "https://github.com/pulp-platform/common_cells.git"
dependencies:
- common_verification
- tech_cells_generic
common_verification:
revision: 6fc76fb013315af9fabbb90b431863d498df2d6d
version: 0.2.0
source:
Git: "https://github.com/pulp-platform/common_verification.git"
dependencies: []
tech_cells_generic:
revision: 203038f857158ae4634c47ce0281f402cc2a1344
version: 0.2.4
source:
Git: "https://github.com/pulp-platform/tech_cells_generic.git"
dependencies:
- common_verification
28 changes: 28 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package:
name: idma
authors:
- "Thomas Benz <tbenz@iis.ee.ethz.ch>" # current maintainer

dependencies:
common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.21.0 }
common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.0 }
axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.29.1 }

sources:
# Source files grouped in levels. Files in level 0 have no dependencies on files in this
# package. Files in level 1 only depend on files in level 0, files in level 2 on files in
# levels 1 and 0, etc. Files within a level are ordered alphabetically.
# Level 0
- src/axi_dma_data_path.sv
# Level 1
- src/axi_dma_data_mover.sv
- src/axi_dma_burst_reshaper.sv
# Level 2
- src/axi_dma_backend.sv

- target: test
files:
# Level 0:
- test/fixture_axi_dma_backend.sv
# Level 1:
- test/tb_axi_dma_backend.sv
43 changes: 43 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
GIT ?= git
BENDER ?= bender
VSIM ?= vsim
PYTHON ?= python3

all: sim_all

clean: sim_clean

# Ensure half-built targets are purged
.DELETE_ON_ERROR:

# --------------
# RTL SIMULATION
# --------------

VLOG_ARGS += -suppress vlog-2583 -suppress vlog-13314 -suppress vlog-13233 -timescale \"1 ns / 1 ps\"
XVLOG_ARGS += -64bit -compile -vtimescale 1ns/1ns -quiet

define generate_vsim
echo 'set ROOT [file normalize [file dirname [info script]]/$3]' > $1
bender script $(VSIM) --vlog-arg="$(VLOG_ARGS)" $2 | grep -v "set ROOT" >> $1
echo >> $1
endef

sim_all: scripts/compile.tcl

sim_clean:
rm -rf scripts/compile.tcl
rm -rf work

scripts/compile.tcl: Bender.yml
$(call generate_vsim, $@, -t rtl -t test,..)

# --------------
# TRACER
# --------------

trace:
dma_trace_00000.txt

dma_trace_%.txt: scripts/dma_trace.py scripts/dma_backend.py
$(PYTHON) $< dma_trace_$*.log > $@
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# iDMA

Home of the DMA. Replaces the version on `github.com/axi`!!!!

## Build for Simulation
`sh> make sim_all`


## Simulate
`sh> vsim -64 &` \
`vsim> source scripts/compile.tcl` \
`vsim> source scripts/start.tcl` \
`vsim> run -all`


### Trace
`sh> make trace`

Empty file added doc/.gitkeep
Empty file.
242 changes: 242 additions & 0 deletions scripts/dma_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# Copyright (c) 2020 ETH Zurich, University of Bologna
# All rights reserved.
#
# This code is under development and not yet released to the public.
# Until it is released, the code is under the copyright of ETH Zurich and
# the University of Bologna, and may contain confidential and/or unpublished
# work. Any reuse/redistribution is strictly forbidden without written
# permission from ETH Zurich.

# DMA backend model
# experimental code
# this could should not be used by anyone,
# especially not in production environments

# split address in bus address and offset
def address_split (addr, data_width = 512):
bytes_per_word = data_width // 8
# bus-aligned address
bus_addr = (addr // bytes_per_word) * bytes_per_word
# word offset
offset = addr % bytes_per_word
return [bus_addr, offset]


# number ob bytes until next page crossing
def bytes_to_page_crossing (addr, deburst, data_width = 512):
if not deburst:
# pages are 4kiB
offset = addr % 4096
return 4096 - offset;
else:
bus_width = data_width // 8
# pages are only the size of the bus to deburst
offset = addr % bus_width
return bus_width - offset;


# combine read and write pages
def bytes_to_page_crossings (src, dst, deburst):
src_bytes = bytes_to_page_crossing(src, deburst)
dst_bytes = bytes_to_page_crossing(dst, deburst)
cmb_bytes = min(src_bytes, dst_bytes)

return [src_bytes, dst_bytes, cmb_bytes]


# number of bytes possible in current situation
def bytes_possible (src, dst, decouple, deburst, data_width = 512):
bus_width = data_width // 8
# how many bytes are possible in next burst?
[src_bytes, dst_bytes, cmb_bytes] = bytes_to_page_crossings(src, dst, deburst)
if not deburst:
if decouple:
src_bytes = min(src_bytes, 4096)
dst_bytes = min(dst_bytes, 4096)
else:
src_bytes = min(cmb_bytes, 4096)
dst_bytes = src_bytes
return [src_bytes, dst_bytes]
else:
if decouple:
src_bytes = min(src_bytes, bus_width)
dst_bytes = min(dst_bytes, bus_width)
else:
src_bytes = min(cmb_bytes, bus_width)
dst_bytes = src_bytes
return [src_bytes, dst_bytes]


# how many bytes are left in transfer
def bytes_left (src, dst, num_bytes_src, num_bytes_dst, decouple, deburst):
[src_bp, dst_bp] = bytes_possible(src, dst, decouple, deburst)
num_bytes_src = min(num_bytes_src , src_bp)
num_bytes_dst = min(num_bytes_dst , dst_bp)

return [num_bytes_src, num_bytes_dst]


# transform number bytes into number of beats
def bytes_to_beats (num_bytes, deburst, data_width = 512):
bus_width = data_width // 8
if not deburst:
return min(num_bytes // bus_width, 4096 // bus_width, 256)
else:
return min(num_bytes // bus_width, 1, 256)


# calculate transfer tailer
def calc_tailer (num_bytes, data_width = 512):
bus_width = data_width // 8
return (bus_width - (bus_width - num_bytes) % bus_width) % bus_width


# define necessary shift to realign data
def realign_shift (src, dst, data_width = 512):
bus_width = data_width // 8
src_offset = address_split(src)[1]
dst_offset = address_split(dst)[1]
return (dst_offset - src_offset) % bus_width


# create masks
def create_masks (offset, tailer, data_width = 512):
full_mask = 2**(data_width // 8) - 1
first_mask = (full_mask << offset) & full_mask
if (tailer == 0):
last_mask = full_mask
else:
last_mask = ~(full_mask << tailer) & full_mask
single_mask = first_mask & last_mask
return [first_mask, full_mask, last_mask, single_mask]


# barrel shifter
def barrel_shift (shift, mask, granularity = 1, data_width = 512):
bus_width = data_width // 8
shift = shift * granularity
full_mask = 2**(data_width // 8) - 1
res = (mask << shift) & full_mask
res += (mask >> (bus_width - shift)) & full_mask
return res


# split transfer in axi-conform read / write requests
def axi_read_writes(src, dst, num_bytes, decouple, deburst):

read_requests = []
write_requests = []

# split read and write pipeline
num_bytes_src = num_bytes
num_bytes_dst = num_bytes

# calculaate the shift
shift = realign_shift(src, dst)

while True:
written = False
# address splitting
[src_bus_addr, src_offset] = address_split(src)
[dst_bus_addr, dst_offset] = address_split(dst)
# bytes left in page / burst
[bytes_left_src, bytes_left_dst] = bytes_left(
src, dst, num_bytes_src, num_bytes_dst, decouple, deburst)

# issue read requests
if num_bytes_src:
num_beats = bytes_to_beats(bytes_left_src + src_offset - 1, deburst)
tailer = calc_tailer(bytes_left_src + src_offset)
read_requests.append( {'addr': src_bus_addr, 'size': num_beats, 'offset': src_offset, 'tailer': tailer, 'shift': shift} )
src += bytes_left_src
num_bytes_src -= bytes_left_src
written = True

# issue read requests
if num_bytes_dst:
num_beats = bytes_to_beats(bytes_left_dst + dst_offset - 1, deburst)
tailer = calc_tailer(bytes_left_dst + dst_offset)
write_requests.append( {'addr': dst_bus_addr, 'size': num_beats, 'offset': dst_offset, 'tailer': tailer} )
dst += bytes_left_dst
num_bytes_dst -= bytes_left_dst
written = True

if not written:
break

return [read_requests, write_requests]


# return a list of reads
def data_path_read (read_requests):

reads = []

# iterate over the requests
for read_request in read_requests:
# calculate masks
masks = create_masks(read_request['offset'], read_request['tailer'])

# issue reads
for b in range(0, read_request['size'] + 1):
# single transfer
if (read_request['size'] == 0):
mask = masks[3]
else:
# first read
if (b == 0):
mask = masks[0]
# last transfer
elif (b == read_request['size']):
mask = masks[2]
else:
mask = masks[1]

write_aligned_mask = barrel_shift(read_request['shift'], mask)
# append masks to read
reads.append( {'r_mask': mask, 'wa_mask': write_aligned_mask} )

return reads


# return a list of writes
def data_path_write (write_requests):

writes = []

# iterate over the requests
for write_request in write_requests:
# calculate masks
masks = create_masks(write_request['offset'], write_request['tailer'])

# issue writes
for b in range(0, write_request['size'] + 1):
# single transfer
if (write_request['size'] == 0):
mask = masks[3]
else:
# first write
if (b == 0):
mask = masks[0]
# last transfer
elif (b == write_request['size']):
mask = masks[2]
else:
mask = masks[1]

# append masks to writes
writes.append( {'w_mask': mask } )

return writes


# split a 1D request in AW/AR/R/W like transfers
def dma_backend (src, dst, num_bytes, decouple, deburst):

[read_request, write_request] = axi_read_writes(src, dst, num_bytes, decouple, deburst)

reads = data_path_read(read_request)
writes = data_path_write(write_request)

return [read_request, write_request, reads, writes]

Loading

0 comments on commit a2abe20

Please sign in to comment.