1
- from calyx .builder import (
2
- Builder ,
3
- add_comp_ports ,
4
- invoke ,
5
- par ,
6
- while_ ,
7
- if_
8
- )
1
+ # Implements an AXI controller which dynamically reads and writes data
2
+ # in and out of the computational kernel as needed. Compare with the
3
+ # read-compute-write implementation in the original `axi_generator`.
4
+
5
+ from calyx .builder import Builder , add_comp_ports , invoke , par , while_ , if_
9
6
from axi_controller_generator import add_control_subordinate
10
7
from typing import Literal
11
8
from math import log2 , ceil
21
18
width_key = "data_width"
22
19
size_key = "total_size"
23
20
name_key = "name"
24
- #This returns an array based on dimensions of memory
21
+ # This returns an array based on dimensions of memory
25
22
address_width_key = "idx_sizes"
26
23
type_key = "memory_type"
27
24
28
- #TODO (nathanielnrn): Should we make these comb groups?
25
+
26
+ # TODO (nathanielnrn): Should we make these comb groups?
29
27
def add_address_translator (prog , mem ):
30
28
address_width = mem [address_width_key ][0 ]
31
29
data_width = mem [width_key ]
@@ -36,13 +34,14 @@ def add_address_translator(prog, mem):
36
34
translator_output = [("axi_address" , 64 )]
37
35
add_comp_ports (address_translator , translator_inputs , translator_output )
38
36
39
- #Cells
40
- #XRT expects 64 bit address.
41
- address_mult = address_translator .const_mult (64 , width_in_bytes (data_width ), f"mul_{ name } " )
37
+ # Cells
38
+ # XRT expects 64 bit address.
39
+ address_mult = address_translator .const_mult (
40
+ 64 , width_in_bytes (data_width ), f"mul_{ name } "
41
+ )
42
42
pad_input_addr = address_translator .pad (address_width , 64 , f"pad_input_addr" )
43
43
44
-
45
- #Assignment
44
+ # Assignment
46
45
with address_translator .continuous :
47
46
pad_input_addr .in_ = address_translator .this ()["calyx_mem_addr" ]
48
47
address_mult .in_ = pad_input_addr .out
@@ -113,12 +112,14 @@ def _add_m_to_s_address_channel(prog, mem, prefix: Literal["AW", "AR"]):
113
112
xhandshake_occurred .write_en = (~ xhandshake_occurred .out ) @ 1
114
113
115
114
# Drive output signals for transfer
116
- m_to_s_address_channel .this ()[f"{ x } ADDR" ] = m_to_s_address_channel .this ()["axi_address" ]
115
+ m_to_s_address_channel .this ()[f"{ x } ADDR" ] = m_to_s_address_channel .this ()[
116
+ "axi_address"
117
+ ]
117
118
# This is taken from mem size, we assume the databus width is the size
118
119
# of our memory cell and that width is a power of 2
119
120
# TODO(nathanielnrn): convert to binary instead of decimal
120
121
m_to_s_address_channel .this ()[f"{ x } SIZE" ] = width_xsize (mem [width_key ])
121
- #Dynamic accesses only need asingle transfer per transcation
122
+ # Dynamic accesses only need asingle transfer per transcation
122
123
m_to_s_address_channel .this ()[f"{ x } LEN" ] = 0
123
124
m_to_s_address_channel .this ()[f"{ x } BURST" ] = 1 # Must be INCR for XRT
124
125
# Required by spec, we hardcode to privileged, non-secure, data access
@@ -130,12 +131,10 @@ def _add_m_to_s_address_channel(prog, mem, prefix: Literal["AW", "AR"]):
130
131
bt_reg .write_en = 1
131
132
do_x_transfer .done = bt_reg .out
132
133
133
-
134
134
# ARLEN must be between 0-255, make sure to subtract 1 from yxi
135
135
# size when assigning to ARLEN
136
136
# assert mem[size_key] < 256, "Memory size must be less than 256"
137
137
138
-
139
138
m_to_s_address_channel .control += [
140
139
par (
141
140
invoke (bt_reg , in_in = 0 ),
@@ -227,7 +226,7 @@ def add_read_channel(prog, mem):
227
226
# Control
228
227
invoke_n_RLAST = invoke (n_RLAST , in_in = 1 )
229
228
# invoke_bt_reg = invoke(bt_reg, in_in=0)
230
-
229
+
231
230
# Could arguably get rid of this while loop for the dynamic verison, but this
232
231
# matches nicely with non dynamic version and conforms to spec,
233
232
# and will be easier to extend to variable length dynamic transfers in the future
@@ -245,11 +244,7 @@ def add_write_channel(prog, mem):
245
244
name = mem [name_key ]
246
245
# Inputs/Outputs
247
246
write_channel = prog .component (f"m_write_channel_{ name } " )
248
- channel_inputs = [
249
- ("ARESETn" , 1 ),
250
- ("WREADY" , 1 ),
251
- ("write_data" , data_width )
252
- ]
247
+ channel_inputs = [("ARESETn" , 1 ), ("WREADY" , 1 ), ("write_data" , data_width )]
253
248
# TODO(nathanielnrn): We currently assume WDATA is the same width as the
254
249
# memory. This limits throughput many AXI data busses are much wider
255
250
# i.e., 512 bits.
@@ -294,7 +289,7 @@ def add_write_channel(prog, mem):
294
289
write_channel .this ()["WLAST" ] = 1
295
290
296
291
# done after handshake
297
- #TODO(nathanielnrn): Perhaps we can combine between handshake_occurred and bt_reg
292
+ # TODO(nathanielnrn): Perhaps we can combine between handshake_occurred and bt_reg
298
293
bt_reg .in_ = (wvalid .out & WREADY ) @ 1
299
294
bt_reg .in_ = ~ (wvalid .out & WREADY ) @ 0
300
295
bt_reg .write_en = 1
@@ -350,6 +345,7 @@ def add_bresp_channel(prog, mem):
350
345
# Control
351
346
bresp_channel .control += [invoke (bt_reg , in_in = 0 ), block_transfer ]
352
347
348
+
353
349
def add_read_controller (prog , mem ):
354
350
add_arread_channel (prog , mem )
355
351
add_read_channel (prog , mem )
@@ -377,18 +373,21 @@ def add_read_controller(prog, mem):
377
373
(f"ARBURST" , 2 ),
378
374
(f"ARPROT" , 3 ),
379
375
(f"RREADY" , 1 ),
380
- #sent out to axi_dyn_mem
376
+ # sent out to axi_dyn_mem
381
377
(f"read_data" , data_width ),
382
378
]
383
379
384
380
add_comp_ports (read_controller , read_controller_inputs , read_controller_outputs )
385
381
386
- #Cells
387
- simple_ar_channel = read_controller .cell (f"ar_channel_{ name } " , prog .get_component (f"m_ar_channel_{ name } " ))
388
- simple_read_channel = read_controller .cell (f"read_channel_{ name } " , prog .get_component (f"m_read_channel_{ name } " ))
382
+ # Cells
383
+ simple_ar_channel = read_controller .cell (
384
+ f"ar_channel_{ name } " , prog .get_component (f"m_ar_channel_{ name } " )
385
+ )
386
+ simple_read_channel = read_controller .cell (
387
+ f"read_channel_{ name } " , prog .get_component (f"m_read_channel_{ name } " )
388
+ )
389
389
# No groups necesarry
390
390
391
-
392
391
# Control
393
392
# Invokes
394
393
@@ -422,6 +421,7 @@ def add_read_controller(prog, mem):
422
421
simple_read_invoke ,
423
422
]
424
423
424
+
425
425
def add_write_controller (prog , mem ):
426
426
add_awwrite_channel (prog , mem )
427
427
add_write_channel (prog , mem )
@@ -455,13 +455,18 @@ def add_write_controller(prog, mem):
455
455
456
456
add_comp_ports (write_controller , write_controller_inputs , write_controller_outputs )
457
457
458
- #Cells
459
- simple_aw_channel = write_controller .cell (f"aw_channel_{ name } " , prog .get_component (f"m_aw_channel_{ name } " ))
460
- simple_write_channel = write_controller .cell (f"write_channel_{ name } " , prog .get_component (f"m_write_channel_{ name } " ))
461
- simple_bresp_channel = write_controller .cell (f"bresp_channel_{ name } " , prog .get_component (f"m_bresp_channel_{ name } " ))
458
+ # Cells
459
+ simple_aw_channel = write_controller .cell (
460
+ f"aw_channel_{ name } " , prog .get_component (f"m_aw_channel_{ name } " )
461
+ )
462
+ simple_write_channel = write_controller .cell (
463
+ f"write_channel_{ name } " , prog .get_component (f"m_write_channel_{ name } " )
464
+ )
465
+ simple_bresp_channel = write_controller .cell (
466
+ f"bresp_channel_{ name } " , prog .get_component (f"m_bresp_channel_{ name } " )
467
+ )
462
468
# No groups necesarry
463
469
464
-
465
470
# Control
466
471
# Invokes
467
472
simple_aw_invoke = invoke (
@@ -498,6 +503,7 @@ def add_write_controller(prog, mem):
498
503
simple_bresp_invoke ,
499
504
]
500
505
506
+
501
507
def add_axi_dyn_mem (prog , mem ):
502
508
address_width = mem [address_width_key ][0 ]
503
509
data_width = mem [width_key ]
@@ -506,7 +512,7 @@ def add_axi_dyn_mem(prog, mem):
506
512
prog .import_ ("primitives/memories/dyn.futil" )
507
513
axi_dyn_mem = prog .component (f"axi_dyn_mem_{ name } " )
508
514
# Inputs/Outputs
509
- dyn_mem_inputs = [
515
+ dyn_mem_inputs = [
510
516
("addr0" , address_width , [("write_together" , 1 ), "data" ]),
511
517
("content_en" , 1 , [("write_together" , 1 ), ("go" , 1 )]),
512
518
("write_en" , 1 , [("write_together" , 2 )]),
@@ -606,13 +612,12 @@ def add_axi_dyn_mem(prog, mem):
606
612
out_WDATA = this_component ["WDATA" ],
607
613
out_BREADY = this_component ["BREADY" ],
608
614
)
609
-
615
+
610
616
axi_dyn_mem .control += [
611
617
latch_write_en ,
612
618
if_ (write_en_reg .out , write_controller_invoke , read_controller_invoke )
613
619
]
614
620
615
-
616
621
617
622
# NOTE: Unlike the channel functions, this can expect multiple mems
618
623
def add_wrapper_comp (prog , mems ):
@@ -746,7 +751,10 @@ def add_wrapper_comp(prog, mems):
746
751
747
752
# TODO: Don't think these need to be marked external, but we
748
753
# we need to raise them at some point form original calyx program
749
- axi_mem = wrapper_comp .cell (f"axi_dyn_mem_{ mem_name } " , prog .get_component (f"axi_dyn_mem_{ mem_name } " ))
754
+ axi_mem = wrapper_comp .cell (
755
+ f"axi_dyn_mem_{ mem_name } " , prog .get_component (f"axi_dyn_mem_{ mem_name } " )
756
+ )
757
+
750
758
# Wires
751
759
752
760
with wrapper_comp .continuous :
@@ -759,7 +767,9 @@ def add_wrapper_comp(prog, mems):
759
767
# Connect wrapper ports with axi_dyn_mem ports
760
768
761
769
# Read controller portion inputs
762
- axi_mem ["ARESETn" ] = wrapper_comp .this ()[f"{ mem_name } _ARESETn" ] #note that both styles work
770
+ axi_mem ["ARESETn" ] = wrapper_comp .this ()[
771
+ f"{ mem_name } _ARESETn"
772
+ ] # note that both styles work
763
773
# wrapper_comp.this()[f"{mem_name}_ARESETn"] = axi_mem["ARESETn"] #note that both styles work
764
774
axi_mem .ARREADY = wrapper_comp .this ()[f"{ mem_name } _ARREADY" ]
765
775
axi_mem .RVALID = wrapper_comp .this ()[f"{ mem_name } _RVALID" ]
@@ -876,7 +886,9 @@ def check_mems_wellformed(mems):
876
886
mem [width_key ]
877
887
).is_integer (), "Width must be a power of 2 to be correctly described by xSIZE"
878
888
assert mem [size_key ] > 0 , "Memory size must be greater than 0"
879
- assert mem [type_key ] == "Dynamic" , "Only dynamic memories are currently supported for dynamic axi"
889
+ assert (
890
+ mem [type_key ] == "Dynamic"
891
+ ), "Only dynamic memories are currently supported for dynamic axi"
880
892
881
893
882
894
if __name__ == "__main__" :
@@ -894,4 +906,4 @@ def check_mems_wellformed(mems):
894
906
yxifile = open (yxi_filename )
895
907
yxi = json .load (yxifile )
896
908
mems = yxi ["memories" ]
897
- build ().emit ()
909
+ build ().emit ()
0 commit comments