1 # Basic Implementation of HyperRAM
3 # Copyright (c) 2019 Antti Lukats <antti.lukats@gmail.com>
4 # Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
5 # Copyright (c) 2021 gatecat <gatecat@ds0.me> [nmigen-soc port]
6 # Copyright (C) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
8 # Code from Lukats, Kermarrec and gatecat is Licensed BSD-2-Clause
10 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
11 # under EU Grants 871528 and 957073, and Licensed under the LGPLv3+ License
14 from nmigen
import (Elaboratable
, Module
, Signal
, Record
, Cat
)
15 from nmigen
.cli
import rtlil
17 from nmigen_soc
import wishbone
18 from nmigen_soc
.memory
import MemoryMap
19 from lambdasoc
.periph
import Peripheral
23 def timeline(m
, trigger
, events
):
24 lastevent
= max([e
[0] for e
in events
])
25 counter
= Signal(range(lastevent
+1))
27 # insert counter reset if it doesn't naturally overflow
28 # (test if lastevent+1 is a power of 2)
29 with m
.If(((lastevent
& (lastevent
+ 1)) != 0) & (counter
== lastevent
)):
30 m
.d
.sync
+= counter
.eq(0)
31 with m
.Elif(counter
!= 0):
32 m
.d
.sync
+= counter
.eq(counter
+ 1)
34 m
.d
.sync
+= counter
.eq(1)
38 return trigger
& (counter
== 0)
40 return counter
== e
[0]
42 with m
.If(get_cond(ev
)):
46 # HyperRAM ASIC PHY -----------------------------------------------------------
48 class HyperRAMASICPhy(Elaboratable
):
49 def __init__(self
, io
):
51 self
.clk
= clk
= Signal()
52 self
.cs
= cs
= Signal()
54 self
.dq_o
= dq_o
= Signal(8)
55 self
.dq_i
= dq_i
= Signal(8)
56 self
.dq_oe
= dq_oe
= Signal()
58 self
.rwds_o
= rwds_o
= Signal
.like(self
.io
["rwds_o"])
59 self
.rwds_oe
= rwds_oe
= Signal()
61 def elaborate(self
, platform
):
64 clk
, cs
= self
.clk
, self
.cs
65 dq_o
, dq_i
, dq_oe
= self
.dq_o
, self
.dq_i
, self
.dq_oe
66 rwds_o
, rwds_oe
= self
.rwds_o
, self
.rwds_oe
69 self
.io
["rwds_o"].eq(rwds_o
),
70 self
.io
["csn_o"].eq(~cs
),
71 self
.io
["csn_oe"].eq(0),
72 self
.io
["clk_o"].eq(clk
),
73 self
.io
["clk_oe"].eq(0),
74 self
.io
["rwds_oe"].eq(~rwds_oe
),
79 self
.io
[f
"d{i}_o"].eq(dq_o
[i
]),
80 self
.io
[f
"d{i}_oe"].eq(~dq_oe
),
81 dq_i
[i
].eq(self
.io
[f
"d{i}_i"])
87 return list(self
.io
.fields
.values())
89 # HyperRAM --------------------------------------------------------------------
91 class HyperRAM(Peripheral
, Elaboratable
):
94 Provides a very simple/minimal HyperRAM core that should work with all
96 - FPGA vendor agnostic.
97 - no setup/chip configuration (use default latency).
99 This core favors portability and ease of use over performance.
101 def __init__(self
, *, io
, phy_kls
):
104 self
.phy
= phy_kls(io
)
105 self
.bus
= wishbone
.Interface(addr_width
=21,
106 data_width
=32, granularity
=8)
107 mmap
= MemoryMap(addr_width
=23, data_width
=8)
108 mmap
.add_resource(object(), name
="hyperram", size
=2**23)
109 self
.bus
.memory_map
= mmap
113 def elaborate(self
, platform
):
115 m
.submodules
.phy
= self
.phy
118 clk_phase
= Signal(2)
125 dq_oe
= self
.phy
.dq_oe
127 rwds_o
= self
.phy
.rwds_o
128 rwds_oe
= self
.phy
.rwds_oe
130 # Clock Generation (sys_clk/4) -----------------------------------
131 m
.d
.sync
+= clk_phase
.eq(clk_phase
+ 1)
132 with m
.Switch(clk_phase
):
134 m
.d
.sync
+= clk
.eq(cs
)
136 m
.d
.sync
+= clk
.eq(0)
138 # Data Shift Register (for write and read) ------------------------
140 m
.d
.sync
+= dqi
.eq(dq_i
) # Sample on 90° and 270°
141 with m
.Switch(clk_phase
):
143 m
.d
.sync
+= sr
.eq(Cat(dqi
, sr
[:-8]))
146 self
.bus
.dat_r
.eq(sr
), # To Wisbone
147 dq_o
.eq(sr
[-8:]), # To HyperRAM
150 # Command generation ----------------------------------------------
152 ca
[47].eq(~self
.bus
.we
), # R/W#
153 ca
[45].eq(1), # Burst Type (Linear)
154 ca
[16:35].eq(self
.bus
.adr
[2:21]), # Row & Upper Column Address
155 ca
[1:3].eq(self
.bus
.adr
[0:2]), # Lower Column Address
156 ca
[0].eq(0), # Lower Column Address
159 # Sequencer -------------------------------------------------------
163 (12, [cs
.eq(1), dq_oe
.eq(1), sr
.eq(ca
)]), # Command: 6 clk
164 (44, [dq_oe
.eq(0)]), # Latency(dflt): 2*6 clk
165 (2, [dq_oe
.eq(self
.bus
.we
), # Wr/Rd data byte: 2 clk
167 sr
[16:].eq(self
.bus
.dat_w
),
168 rwds_oe
.eq(self
.bus
.we
),
169 rwds_o
.eq(~self
.bus
.sel
[3])]),
170 (2, [rwds_o
.eq(~self
.bus
.sel
[2])]), # Wr/Rd data byte: 2 clk
171 (2, [rwds_o
.eq(~self
.bus
.sel
[1])]), # Wr/Rd data byte: 2 clk
172 (2, [rwds_o
.eq(~self
.bus
.sel
[0])]), # Wr/Rd data byte: 2 clk
173 (2, [cs
.eq(0), rwds_oe
.eq(0), dq_oe
.eq(0)]),
174 (1, [self
.bus
.ack
.eq(1)]),
175 (1, [self
.bus
.ack
.eq(0)]),
178 # Convert delta-time sequencer to time sequencer
180 t_seq_start
= (clk_phase
== 1)
185 timeline(m
, self
.bus
.cyc
& self
.bus
.stb
& t_seq_start
, t_seq
)
189 return self
.phy
.ports() + list(self
.bus
.fields
.values())
192 if __name__
== '__main__':
193 layout
=[('rwds_o', 1), ('rwds_oe', 1),
194 ('csn_o', 1), ('csn_oe', 1),
195 ('clk_o', 1), ('clk_oe', 1)]
197 layout
+= [('d%d_o' % i
, 1), ('d%d_oe' % i
, 1), ('d%d_i' % i
, 1)]
198 io
= Record(layout
=layout
)
199 dut
= HyperRAM(io
=io
, phy_kls
=HyperRAMASICPhy
)
200 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
201 with
open("test_hyperram.il", "w") as f
: