-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathrom.py
136 lines (122 loc) · 4.56 KB
/
rom.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from nmigen import *
from math import ceil, log2
from nmigen.back.pysim import *
from nmigen_soc.memory import *
from nmigen_soc.wishbone import *
from isa import *
###############
# ROM module: #
###############
class ROM( Elaboratable ):
def __init__( self, data ):
# Data storage.
self.data = Memory( width = 32, depth = len( data ), init = data )
# Memory read port.
self.r = self.data.read_port()
# Record size.
self.size = len( data ) * 4
# Initialize Wishbone bus arbiter.
self.arb = Arbiter( addr_width = ceil( log2( self.size + 1 ) ),
data_width = 32 )
self.arb.bus.memory_map = MemoryMap(
addr_width = self.arb.bus.addr_width,
data_width = self.arb.bus.data_width,
alignment = 0 )
def new_bus( self ):
# Initialize a new Wishbone bus interface.
bus = Interface( addr_width = self.arb.bus.addr_width,
data_width = self.arb.bus.data_width )
bus.memory_map = MemoryMap( addr_width = bus.addr_width,
data_width = bus.data_width,
alignment = 0 )
self.arb.add( bus )
return bus
def elaborate( self, platform ):
m = Module()
m.submodules.arb = self.arb
m.submodules.r = self.r
# Ack two cycles after activation, for memory port access and
# synchronous read-out (to prevent combinatorial loops).
rws = Signal( 1, reset = 0 )
m.d.sync += [
rws.eq( self.arb.bus.cyc ),
self.arb.bus.ack.eq( self.arb.bus.cyc & rws )
]
# Set read port address (in words).
m.d.comb += self.r.addr.eq( self.arb.bus.adr >> 2 )
# Set the 'output' value to the requested 'data' array index.
# If a read would 'spill over' into an out-of-bounds data byte,
# set that byte to 0x00.
# Word-aligned reads
with m.If( ( self.arb.bus.adr & 0b11 ) == 0b00 ):
m.d.sync += self.arb.bus.dat_r.eq( LITTLE_END_L( self.r.data ) )
# Un-aligned reads
with m.Else():
m.d.sync += self.arb.bus.dat_r.eq(
LITTLE_END_L( self.r.data << ( ( self.arb.bus.adr & 0b11 ) << 3 ) ) )
# End of ROM module definition.
return m
##################
# ROM testbench: #
##################
# Keep track of test pass / fail rates.
p = 0
f = 0
# Perform an individual ROM unit test.
def rom_read_ut( rom, address, expected ):
global p, f
# Set address, and wait two ticks.
yield rom.arb.bus.adr.eq( address )
yield Tick()
yield Tick()
# Done. Check the result after combinational logic settles.
yield Settle()
actual = yield rom.arb.bus.dat_r
if expected != actual:
f += 1
print( "\033[31mFAIL:\033[0m ROM[ 0x%08X ] = 0x%08X (got: 0x%08X)"
%( address, expected, actual ) )
else:
p += 1
print( "\033[32mPASS:\033[0m ROM[ 0x%08X ] = 0x%08X"
%( address, expected ) )
# Top-level ROM test method.
def rom_test( rom ):
global p, f
# Let signals settle after reset.
yield Settle()
# Print a test header.
print( "--- ROM Tests ---" )
# Assert 'cyc' to activate the bus.
yield rom.arb.bus.cyc.eq( 1 )
# Test the ROM's "happy path" (reading valid data).
yield from rom_read_ut( rom, 0x0, LITTLE_END( 0x01234567 ) )
yield from rom_read_ut( rom, 0x4, LITTLE_END( 0x89ABCDEF ) )
yield from rom_read_ut( rom, 0x8, LITTLE_END( 0x42424242 ) )
yield from rom_read_ut( rom, 0xC, LITTLE_END( 0xDEADBEEF ) )
# Test byte-aligned and halfword-aligned addresses.
yield from rom_read_ut( rom, 0x1, LITTLE_END( 0x23456700 ) )
yield from rom_read_ut( rom, 0x2, LITTLE_END( 0x45670000 ) )
yield from rom_read_ut( rom, 0x3, LITTLE_END( 0x67000000 ) )
yield from rom_read_ut( rom, 0x5, LITTLE_END( 0xABCDEF00 ) )
yield from rom_read_ut( rom, 0x6, LITTLE_END( 0xCDEF0000 ) )
yield from rom_read_ut( rom, 0x7, LITTLE_END( 0xEF000000 ) )
# Test reading the last few bytes of data.
yield from rom_read_ut( rom, rom.size - 4, LITTLE_END( 0xDEADBEEF ) )
yield from rom_read_ut( rom, rom.size - 3, LITTLE_END( 0xADBEEF00 ) )
yield from rom_read_ut( rom, rom.size - 2, LITTLE_END( 0xBEEF0000 ) )
yield from rom_read_ut( rom, rom.size - 1, LITTLE_END( 0xEF000000 ) )
# Done.
yield Tick()
print( "ROM Tests: %d Passed, %d Failed"%( p, f ) )
# 'main' method to run a basic testbench.
if __name__ == "__main__":
# Instantiate a test ROM module with 16 bytes of data.
dut = ROM( [ 0x01234567, 0x89ABCDEF, 0x42424242, 0xDEADBEEF ] )
# Run the ROM tests.
with Simulator( dut, vcd_file = open( 'rom.vcd', 'w' ) ) as sim:
def proc():
yield from rom_test( dut )
sim.add_clock( 1e-6 )
sim.add_sync_process( proc )
sim.run()