1 # Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
2 # Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Copyright (C) 2022 Raptor Engineering, LLC <support@raptorengineering.com>
5 # Based on code from LambaConcept, from the gram example which is BSD-2-License
6 # https://github.com/jeanthom/gram/tree/master/examples
8 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
9 # under EU Grants 871528 and 957073, under the LGPLv3+ License
11 from nmigen
import (Module
, Elaboratable
, DomainRenamer
, Record
,
12 Signal
, Cat
, Const
, ClockSignal
, ResetSignal
,
14 from nmigen
.build
.dsl
import Attrs
15 from nmigen
.cli
import verilog
16 from nmigen
.lib
.cdc
import ResetSynchronizer
17 from nmigen_soc
import wishbone
, memory
18 from nmigen_soc
.memory
import MemoryMap
19 from nmigen
.utils
import log2_int
20 from nmigen_boards
.resources
.interface
import UARTResource
21 from nmigen_stdio
.serial
import AsyncSerial
24 from nmigen_boards
.resources
.memory
import HyperRAMResource
25 from lambdasoc
.periph
.hyperram
import HyperRAM
, HyperRAMPads
, HyperRAMPHY
27 from lambdasoc
.periph
.event
import IRQLine
28 from lambdasoc
.periph
.intc
import GenericInterruptController
29 from lambdasoc
.periph
.sram
import SRAMPeripheral
30 from lambdasoc
.periph
.timer
import TimerPeripheral
31 from lambdasoc
.periph
import Peripheral
32 from lambdasoc
.soc
.base
import SoC
33 from soc
.bus
.uart_16550
import UART16550
# opencores 16550 uart
34 from soc
.bus
.tercel
import Tercel
# SPI XIP master
35 from soc
.bus
.opencores_ethmac
import EthMAC
# OpenCores 10/100 Ethernet MAC
36 from soc
.bus
.external_core
import ExternalCore
# external libresoc/microwatt
37 from soc
.bus
.wb_downconvert
import WishboneDownConvert
38 from soc
.bus
.wb_async
import WBAsyncBridge
39 from soc
.bus
.syscon
import MicrowattSYSCON
40 from soc
.interrupts
.xics
import XICS_ICP
, XICS_ICS
43 from gram
.common
import (PhySettings
, get_cl_cw
, get_sys_latency
,
45 from gram
.core
import gramCore
46 from gram
.phy
.ecp5ddrphy
import ECP5DDRPHY
47 from gram
.phy
.fakephy
import FakePHY
, SDRAM_VERBOSE_STD
, SDRAM_VERBOSE_DBG
48 from gram
.modules
import MT41K256M16
, MT41K64M16
49 from gram
.frontend
.wishbone
import gramWishbone
52 from nmigen
.build
import Resource
53 from nmigen
.build
import Subsignal
54 from nmigen
.build
import Pins
56 # Board (and simulation) platforms
57 from nmigen_boards
.versa_ecp5
import VersaECP5Platform
58 from nmigen_boards
.versa_ecp5
import VersaECP5Platform85
# custom board
59 from nmigen_boards
.ulx3s
import ULX3S_85F_Platform
60 from nmigen_boards
.arty_a7
import ArtyA7_100Platform
61 from nmigen_boards
.test
.blinky
import Blinky
62 from nmigen_boards
.orangecrab_r0_2
import OrangeCrabR0_2_85k_Platform
63 from icarusversa
import IcarusVersaPlatform
64 # Clock-Reset Generator (works for all ECP5 platforms)
65 from ecp5_crg
import ECP5CRG
66 from arty_crg
import ArtyA7CRG
71 def sim_ddr3_settings(clk_freq
=100e6
):
72 tck
= 2/(2*2*clk_freq
)
78 cl
, cwl
= get_cl_cw("DDR3", tck
)
79 cl_sys_latency
= get_sys_latency(nphases
, cl
)
80 cwl_sys_latency
= get_sys_latency(nphases
, cwl
)
81 rdcmdphase
, rdphase
= get_sys_phases(nphases
, cl_sys_latency
, cl
)
82 wrcmdphase
, wrphase
= get_sys_phases(nphases
, cwl_sys_latency
, cwl
)
87 dfi_databits
=4*databits
,
92 rdcmdphase
=rdcmdphase
,
93 wrcmdphase
=wrcmdphase
,
96 read_latency
=2 + cl_sys_latency
+ 2 + log2_int(4//nphases
) + 4,
97 write_latency
=cwl_sys_latency
101 class WB64to32Convert(Elaboratable
):
102 """Microwatt IO wishbone slave 64->32 bits converter
104 For timing reasons, this adds a one cycle latch on the way both
105 in and out. This relaxes timing and routing pressure on the "main"
106 memory bus by moving all simple IOs to a slower 32-bit bus.
108 This implementation is rather dumb at the moment, no stash buffer,
109 so we stall whenever that latch is busy. This can be improved.
111 def __init__(self
, master
, slave
):
115 def elaborate(self
, platform
):
117 comb
, sync
= m
.d
.comb
, m
.d
.sync
118 master
, slave
= self
.master
, self
.slave
125 with m
.State("IDLE"):
126 # Clear ACK (and has_top_r) in case it was set
127 sync
+= master
.ack
.eq(0)
128 sync
+= has_top_r
.eq(0)
130 # Do we have a cycle ?
131 with m
.If(master
.cyc
& master
.stb
):
132 # Stall master until we are done, we are't (yet) pipelining
133 # this, it's all slow IOs.
134 sync
+= master
.stall
.eq(1)
136 # Start cycle downstream
137 sync
+= slave
.cyc
.eq(1)
138 sync
+= slave
.stb
.eq(1)
140 # Do we have a top word and/or a bottom word ?
141 comb
+= has_top
.eq(master
.sel
[4:].bool())
142 comb
+= has_bot
.eq(master
.sel
[:4].bool())
143 # record the has_top flag for the next FSM state
144 sync
+= has_top_r
.eq(has_top
)
146 # Copy write enable to IO out, copy address as well,
147 # LSB is set later based on HI/LO
148 sync
+= slave
.we
.eq(master
.we
)
149 sync
+= slave
.adr
.eq(Cat(0, master
.adr
))
151 # If we have a bottom word, handle it first, otherwise
152 # send the top word down. XXX Split the actual mux out
153 # and only generate a control signal.
155 with m
.If(master
.we
):
156 sync
+= slave
.dat_w
.eq(master
.dat_w
[:32])
157 sync
+= slave
.sel
.eq(master
.sel
[:4])
159 # Wait for ack on BOTTOM half
160 m
.next
= "WAIT_ACK_BOT"
163 with m
.If(master
.we
):
164 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
165 sync
+= slave
.sel
.eq(master
.sel
[4:])
167 # Bump LSB of address
168 sync
+= slave
.adr
[0].eq(1)
170 # Wait for ack on TOP half
171 m
.next
= "WAIT_ACK_TOP"
174 with m
.State("WAIT_ACK_BOT"):
175 # If we aren't stalled by the device, clear stb
176 if hasattr(slave
, "stall"):
177 with m
.If(~slave
.stall
):
178 sync
+= slave
.stb
.eq(0)
181 with m
.If(slave
.ack
):
182 # If it's a read, latch the data
183 with m
.If(~slave
.we
):
184 sync
+= master
.dat_r
[:32].eq(slave
.dat_r
)
186 # Do we have a "top" part as well ?
187 with m
.If(has_top_r
):
189 with m
.If(master
.we
):
190 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
191 sync
+= slave
.sel
.eq(master
.sel
[4:])
193 # Bump address and set STB
194 sync
+= slave
.adr
[0].eq(1)
195 sync
+= slave
.stb
.eq(1)
198 m
.next
= "WAIT_ACK_TOP"
201 # We are done, ack up, clear cyc downstram
202 sync
+= slave
.cyc
.eq(0)
203 sync
+= slave
.stb
.eq(0)
205 # And ack & unstall upstream
206 sync
+= master
.ack
.eq(1)
207 if hasattr(master
, "stall"):
208 sync
+= master
.stall
.eq(0)
213 with m
.State("WAIT_ACK_TOP"):
214 # If we aren't stalled by the device, clear stb
215 if hasattr(slave
, "stall"):
216 with m
.If(~slave
.stall
):
217 sync
+= slave
.stb
.eq(0)
220 with m
.If(slave
.ack
):
221 # If it's a read, latch the data
222 with m
.If(~slave
.we
):
223 sync
+= master
.dat_r
[32:].eq(slave
.dat_r
)
225 # We are done, ack up, clear cyc downstram
226 sync
+= slave
.cyc
.eq(0)
227 sync
+= slave
.stb
.eq(0)
229 # And ack & unstall upstream
230 sync
+= master
.ack
.eq(1)
231 if hasattr(master
, "stall"):
232 sync
+= master
.stall
.eq(0)
240 class DDR3SoC(SoC
, Elaboratable
):
241 def __init__(self
, *,
244 uart_pins
=None, spi_0_pins
=None, ethmac_0_pins
=None,
245 ddr_pins
=None, ddrphy_addr
=None,
246 dramcore_addr
=None, ddr_addr
=None,
247 fw_addr
=0x0000_0000, firmware
=None,
248 uart_addr
=None, uart_irqno
=0,
249 spi0_addr
=None, spi0_cfg_addr
=None,
250 eth0_cfg_addr
=None, eth0_irqno
=None,
253 xics_icp_addr
=None, xics_ics_addr
=None,
258 # wishbone routing is as follows:
269 # arbiter------------------------------------------------------+
271 # +---decoder----+--------+---------------+-------------+--------+ |
273 # | | | WBAsyncBridge | | | |
275 # uart XICS CSRs DRAM XIP SPI HyperRAM EthMAC
277 # set up wishbone bus arbiter and decoder. arbiter routes,
278 # decoder maps local-relative addressed satellites to global addresses
279 self
._arbiter
= wishbone
.Arbiter(addr_width
=30, data_width
=32,
281 features
={"cti", "bte", "stall"})
282 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32,
284 features
={"cti", "bte", "stall"})
286 # default firmware name
288 firmware
= "firmware/main.bin"
290 # set up clock request generator
293 if fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s',
298 self
.crg
= ECP5CRG(clk_freq
, dram_clk_freq
=dram_clk_freq
,
299 pod_bits
=pod_bits
, sync_bits
=sync_bits
)
300 if fpga
in ['arty_a7']:
301 self
.crg
= ArtyA7CRG(clk_freq
)
303 self
.dram_clk_freq
= dram_clk_freq
304 if self
.dram_clk_freq
is None:
305 self
.dram_clk_freq
= clk_freq
307 # set up CPU, with 64-to-32-bit downconverters, and a delayed Reset
309 self
.cpu
= ExternalCore(name
="ext_core")
311 cvtdbus
= wishbone
.Interface(addr_width
=30, data_width
=32,
312 granularity
=8, features
={'stall'})
313 cvtibus
= wishbone
.Interface(addr_width
=30, data_width
=32,
314 granularity
=8, features
={'stall'})
315 self
.dbusdowncvt
= WB64to32Convert(self
.cpu
.dbus
, cvtdbus
)
316 self
.ibusdowncvt
= WB64to32Convert(self
.cpu
.ibus
, cvtibus
)
317 self
._arbiter
.add(cvtibus
) # I-Cache Master
318 self
._arbiter
.add(cvtdbus
) # D-Cache Master. TODO JTAG master
319 self
.cvtibus
= cvtibus
320 self
.cvtdbus
= cvtdbus
322 # CPU interrupt controller, needs stall to be added, also
323 # compat with wishbone.Interface
324 self
.intc
= GenericInterruptController(width
=len(self
.cpu
.irq
))
325 self
.xics_icp
= icp
= XICS_ICP()
326 self
.xics_ics
= ics
= XICS_ICS()
327 self
.int_level_i
= self
.xics_ics
.int_level_i
329 self
.pbus
= pbus
= wishbone
.Interface(name
="xics_icp_bus",
330 addr_width
=6, data_width
=32,
331 granularity
=8, features
={'stall'})
332 self
.sbus
= sbus
= wishbone
.Interface(name
="xics_ics_bus",
333 addr_width
=10, data_width
=32,
334 granularity
=8, features
={'stall'})
335 pmap
= MemoryMap(addr_width
=8, data_width
=8, name
="icp_map")
336 pbus
.memory_map
= pmap
337 self
._decoder
.add(pbus
, addr
=xics_icp_addr
) # ICP addr
339 smap
= MemoryMap(addr_width
=12, data_width
=8, name
="ics_map")
340 sbus
.memory_map
= smap
341 self
._decoder
.add(sbus
, addr
=xics_ics_addr
) # ICP addr
344 # SRAM (but actually a ROM, for firmware)
345 if fw_addr
is not None:
346 print ("fw at address %x" % fw_addr
)
348 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
350 if firmware
is not None:
351 with
open(firmware
, "rb") as f
:
352 words
= iter(lambda: f
.read(sram_width
// 8), b
'')
353 bios
= [int.from_bytes(w
, "little") for w
in words
]
354 self
.bootmem
.init
= bios
355 self
._decoder
.add(self
.bootmem
.bus
, addr
=fw_addr
) # ROM at fw_addr
357 # System Configuration info
358 # offset executable ELF payload at 6 megabyte offset (2<<20)
359 spi_offset
= 2<<20 if (spi_0_pins
is not None) else None
360 dram_offset
= ddr_addr
if (ddr_pins
is not None) else None
361 self
.syscon
= MicrowattSYSCON(sys_clk_freq
=clk_freq
,
362 mem_clk_freq
=self
.dram_clk_freq
,
363 has_uart
=(uart_pins
is not None),
364 spi_offset
=spi_offset
,
365 dram_addr
=dram_offset
)
366 self
._decoder
.add(self
.syscon
.bus
, addr
=0xc0000000) # at 0xc000_0000
369 # SRAM (read-writeable BRAM)
370 self
.ram
= SRAMPeripheral(size
=4096)
371 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
373 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
374 if uart_pins
is not None:
375 # sigh actual UART in microwatt is 8-bit
376 self
.uart_irq
= IRQLine()
377 self
.uart
= UART16550(data_width
=8, pins
=uart_pins
,
380 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
381 # strictly speaking this is a nmigen-soc "sparse" arrangement
382 # which should be handled by MemoryMap, but needs investigation
383 cvtuartbus
= wishbone
.Interface(addr_width
=5, data_width
=32,
386 umap
= MemoryMap(addr_width
=7, data_width
=8, name
="uart_map")
387 cvtuartbus
.memory_map
= umap
388 self
._decoder
.add(cvtuartbus
, addr
=uart_addr
) # 16550 UART addr
389 self
.cvtuartbus
= cvtuartbus
390 self
.intc
.add_irq(self
.uart
.irq
, index
=uart_irqno
)
392 # SDRAM module using opencores sdr_ctrl
394 class MT48LC16M16(SDRModule):
400 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
404 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
412 # DRAM Module. first, create the (triple) modules:
414 # * gram Core: presents PHY with a DFI Interface
415 # * gram Bone (aka gram-with-wishbone) connects wishbone to DFI
416 # from there it gets a little complicated because of supporting
417 # several options: simulation, synchronous, and asynchronous clocks.
418 # dram_clk_freq can *never* be set equal to clk_freq, if it is,
419 # it's assumed to be synchronous, and the dram Domains need renaming
421 if ddr_pins
is not None: # or fpga == 'sim':
422 ddrmodule
= dram_cls(self
.dram_clk_freq
, "1:2") # match DDR3 P/N
424 # remap both the sync domain (wherever it occurs) and
425 # the sync2x domain, if dram frequency is specified and
426 # not equal to the core clock
428 if dram_clk_freq
is not None or fpga
== 'sim':
431 drs
= DomainRenamer({"sync": "dramsync",
432 "sync2x": "dramsync2x"})
435 if dram_clk_freq
is None:
436 features
.add("stall")
438 # create the PHY (fake one for sim)
440 settings
= sim_ddr3_settings(self
.dram_clk_freq
)
441 self
.ddrphy
= FakePHY(module
=ddrmodule
,
443 verbosity
=SDRAM_VERBOSE_DBG
,
444 clk_freq
=self
.dram_clk_freq
)
446 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
,
448 sys_clk_freq
=self
.dram_clk_freq
))
450 # create the core (bridge from PHY to DFI)
451 dramcore
= gramCore(phy
=self
.ddrphy
,
452 geom_settings
=ddrmodule
.geom_settings
,
453 timing_settings
=ddrmodule
.timing_settings
,
455 clk_freq
=self
.dram_clk_freq
)
456 self
.dramcore
= drs(dramcore
)
458 # create the wishbone presentation (wishbone to DFI)
459 drambone
= gramWishbone(dramcore
, features
=features
)
460 self
.drambone
= drs(drambone
)
462 # this is the case where sys_clk === dram_clk. no ASync Bridge
463 # needed, so just let the phy core and wb-dfi be connected
464 # directly to WB decoder. both are running in "sync" domain
465 # (because of the DomainRenamer, above)
467 if ddr_pins
is not None and dram_clk_freq
is None:
468 self
.ddrphy_bus
= self
.ddrphy
.bus
469 self
.dramcore_bus
= self
.dramcore
.bus
470 self
.drambone_bus
= self
.drambone
.bus
472 # this covers the case where sys_clk != dram_clk: three separate
473 # ASync Bridges are constructed (!) and the interface that's to
474 # be wired to the WB decoder is the async bus because that's running
475 # in the "sync" domain.
477 if ddr_pins
is not None and dram_clk_freq
is not None:
478 # Set up Wishbone asynchronous bridge
479 pabus
= wishbone
.Interface(addr_width
=self
.ddrphy
.bus
.addr_width
,
480 data_width
=self
.ddrphy
.bus
.data_width
,
481 granularity
=self
.ddrphy
.bus
.granularity
,
483 self
.ddrphy_bus
= pabus
484 self
.ddrphy_bus
.memory_map
= self
.ddrphy
.bus
.memory_map
486 pabr
= WBAsyncBridge(master_bus
=self
.ddrphy_bus
,
487 slave_bus
=self
.ddrphy
.bus
,
488 master_clock_domain
=None,
489 slave_clock_domain
="dramsync",
490 address_width
=self
.ddrphy
.bus
.addr_width
,
491 data_width
=self
.ddrphy
.bus
.data_width
,
492 granularity
=self
.ddrphy
.bus
.granularity
)
493 self
.ddrphy_async_br
= pabr
495 # Set up Wishbone asynchronous bridge
496 dab
= wishbone
.Interface(addr_width
=self
.dramcore
.bus
.addr_width
,
497 data_width
=self
.dramcore
.bus
.data_width
,
498 granularity
=self
.dramcore
.bus
.granularity
,
500 self
.dramcore_bus
= dab
501 self
.dramcore_bus
.memory_map
= self
.dramcore
.bus
.memory_map
503 dac
= WBAsyncBridge(master_bus
=self
.dramcore_bus
,
504 slave_bus
=self
.dramcore
.bus
,
505 master_clock_domain
=None,
506 slave_clock_domain
="dramsync",
507 address_width
=self
.dramcore
.bus
.addr_width
,
508 data_width
=self
.dramcore
.bus
.data_width
,
509 granularity
=self
.dramcore
.bus
.granularity
)
510 self
.dramcore_async_br
= dac
512 # Set up Wishbone asynchronous bridge
513 bab
= wishbone
.Interface(addr_width
=self
.drambone
.bus
.addr_width
,
514 data_width
=self
.drambone
.bus
.data_width
,
515 granularity
=self
.drambone
.bus
.granularity
,
517 self
.drambone_bus
= bab
518 self
.drambone_bus
.memory_map
= self
.drambone
.bus
.memory_map
520 bab
= WBAsyncBridge(master_bus
=self
.drambone_bus
,
521 slave_bus
=self
.drambone
.bus
,
522 master_clock_domain
=None,
523 slave_clock_domain
="dramsync",
524 address_width
=self
.drambone
.bus
.addr_width
,
525 data_width
=self
.drambone
.bus
.data_width
,
526 granularity
=self
.drambone
.bus
.granularity
)
527 self
.drambone_async_br
= bab
529 if ddr_pins
is not None:
530 # Add wishbone decoders
531 self
._decoder
.add(self
.dramcore_bus
, addr
=dramcore_addr
)
532 self
._decoder
.add(self
.drambone_bus
, addr
=ddr_addr
)
533 self
._decoder
.add(self
.ddrphy_bus
, addr
=ddrphy_addr
)
535 # additional SRAM at address if DRAM is not also at 0x0
536 # (TODO, check Flash, and HyperRAM as well)
537 if (ddr_pins
is None or ddr_addr
!= 0x0) and fw_addr
!= 0:
538 print ("SRAM 0x8000 at address 0x0")
540 self
.sram
= SRAMPeripheral(size
=0x8000,
541 data_width
=sram_width
,
543 self
._decoder
.add(self
.sram
.bus
, addr
=0x0) # RAM at 0x0
546 if spi_0_pins
is not None and fpga
in ['sim',
548 'rcs_arctic_tern_bmc_card',
552 # The Lattice ECP5 devices require special handling on the
553 # dedicated SPI clock line, which is shared with the internal
554 # SPI controller used for FPGA bitstream loading.
555 spi0_is_lattice_ecp5_clk
= False
556 if fpga
in ['versa_ecp5',
558 'rcs_arctic_tern_bmc_card',
560 spi0_is_lattice_ecp5_clk
= True
562 # Tercel contains two independent Wishbone regions, a
563 # configuration region and the direct API access region,
564 # Set the SPI 0 access region to 16MB, as the FPGA
565 # bitstream Flash device is unlikely to be larger than this.
566 # The main SPI Flash (SPI 1) should be set to at
567 # least 28 bits (256MB) to allow the use of large 4BA devices.
568 self
.spi0
= Tercel(data_width
=32, spi_region_addr_width
=24,
569 adr_offset
=spi0_addr
,
573 lattice_ecp5_usrmclk
=spi0_is_lattice_ecp5_clk
)
574 self
._decoder
.add(self
.spi0
.bus
, addr
=spi0_addr
)
575 self
._decoder
.add(self
.spi0
.cfg_bus
, addr
=spi0_cfg_addr
)
578 if ethmac_0_pins
is not None and fpga
in ['versa_ecp5',
581 self
.eth_irq
= IRQLine()
582 # The OpenCores Ethernet MAC contains two independent Wishbone
583 # interfaces, a slave (configuration) interface and a master (DMA)
585 self
.eth0
= EthMAC(pins
=ethmac_0_pins
, irq
=self
.eth_irq
)
586 self
._arbiter
.add(self
.eth0
.master_bus
)
587 self
._decoder
.add(self
.eth0
.slave_bus
, addr
=eth0_cfg_addr
)
588 self
.intc
.add_irq(self
.eth0
.irq
, index
=eth0_irqno
)
590 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
591 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
594 for i
, (pins
, hraddr
) in enumerate(zip(hyperram_pins
, hyperram_addr
)):
595 hr
= HyperRAM(io
=pins
, phy_kls
=HyperRAMPHY
,
596 name
="hyperram%d" % i
,
598 latency
=7) # Winbond W956D8MBYA
599 self
._decoder
.add(hr
.bus
, addr
=hraddr
)
600 self
.hyperram
.append(hr
)
602 self
.memory_map
= self
._decoder
.bus
.memory_map
604 self
.clk_freq
= clk_freq
607 def elaborate(self
, platform
):
609 comb
, sync
= m
.d
.comb
, m
.d
.sync
611 # add the peripherals and clock-reset-generator
612 if platform
is not None and hasattr(self
, "crg"):
613 m
.submodules
.sysclk
= self
.crg
615 if hasattr(self
, "sram"):
616 m
.submodules
.sram
= self
.sram
617 if hasattr(self
, "bootmem"):
618 m
.submodules
.bootmem
= self
.bootmem
619 m
.submodules
.syscon
= self
.syscon
620 if hasattr(self
, "ram"):
621 m
.submodules
.ram
= self
.ram
622 if hasattr(self
, "uart"):
623 m
.submodules
.uart
= self
.uart
624 comb
+= self
.uart
.cts_i
.eq(1)
625 comb
+= self
.uart
.dsr_i
.eq(1)
626 comb
+= self
.uart
.ri_i
.eq(0)
627 comb
+= self
.uart
.dcd_i
.eq(1)
628 # sigh connect up the wishbone bus manually to deal with
629 # the mis-match on the data. nmigen-soc "sparse" MemoryMap
630 # should be able to deal with this. TODO, investigate
631 uartbus
= self
.uart
.bus
632 comb
+= uartbus
.adr
.eq(self
.cvtuartbus
.adr
)
633 comb
+= uartbus
.stb
.eq(self
.cvtuartbus
.stb
)
634 comb
+= uartbus
.cyc
.eq(self
.cvtuartbus
.cyc
)
635 comb
+= uartbus
.sel
.eq(self
.cvtuartbus
.sel
)
636 comb
+= uartbus
.we
.eq(self
.cvtuartbus
.we
)
637 comb
+= uartbus
.dat_w
.eq(self
.cvtuartbus
.dat_w
) # drops 8..31
638 comb
+= self
.cvtuartbus
.dat_r
.eq(uartbus
.dat_r
) # drops 8..31
639 comb
+= self
.cvtuartbus
.ack
.eq(uartbus
.ack
)
640 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
641 comb
+= uartbus
.stall
.eq(uartbus
.cyc
& ~uartbus
.ack
)
642 comb
+= self
.cvtuartbus
.stall
.eq(uartbus
.stall
)
643 if hasattr(self
, "cpu"):
644 m
.submodules
.intc
= self
.intc
645 m
.submodules
.extcore
= self
.cpu
646 m
.submodules
.dbuscvt
= self
.dbusdowncvt
647 m
.submodules
.ibuscvt
= self
.ibusdowncvt
649 m
.submodules
.arbiter
= self
._arbiter
650 m
.submodules
.decoder
= self
._decoder
651 if hasattr(self
, "ddrphy"):
652 m
.submodules
.ddrphy
= self
.ddrphy
653 m
.submodules
.dramcore
= self
.dramcore
654 m
.submodules
.drambone
= drambone
= self
.drambone
656 # add async wishbone bridges
657 if hasattr(self
, "ddrphy_async_br"):
658 m
.submodules
.ddrphy_async_br
= self
.ddrphy_async_br
659 if hasattr(self
, "dramcore_async_br"):
660 m
.submodules
.dramcore_async_br
= self
.dramcore_async_br
661 if hasattr(self
, "drambone_async_br"):
662 m
.submodules
.drambone_async_br
= self
.drambone_async_br
664 # grrr, same problem with WB async bridge: not WB4-pipe compliant
665 dab
= self
.ddrphy_bus
666 if hasattr(dab
, "stall"):
667 comb
+= dab
.stall
.eq(dab
.cyc
& ~dab
.ack
)
668 dab
= self
.dramcore_bus
669 if hasattr(dab
, "stall"):
670 comb
+= dab
.stall
.eq(dab
.cyc
& ~dab
.ack
)
671 dab
= self
.drambone_bus
672 comb
+= dab
.stall
.eq(dab
.cyc
& ~dab
.ack
)
674 # add wb async bridge verilog source. assumes directory structure
675 # where bridge has been checked out in a common subdirectory with:
676 # git clone https://github.com/alexforencich/verilog-wishbone.git
677 # git checkout d1fa24a0
678 verilog_wishbone
= "../../verilog-wishbone/rtl"
679 pth
= os
.path
.split(__file__
)[0]
680 pth
= os
.path
.join(pth
, verilog_wishbone
)
681 fname
= os
.path
.abspath(pth
)
683 if hasattr(self
, "ddrphy_async_br"):
684 self
.dramcore_async_br
.add_verilog_source(fname
, platform
)
685 if hasattr(self
, "drambone_async_br"):
686 self
.drambone_async_br
.add_verilog_source(fname
, platform
)
688 # add hyperram module
689 for i
, hr
in enumerate(self
.hyperram
):
690 m
.submodules
["hyperram%d" % i
] = hr
691 # grrr, same problem with hyperram: not WB4-pipe compliant
692 comb
+= hr
.bus
.stall
.eq(hr
.bus
.cyc
& ~hr
.bus
.ack
)
694 if self
.fpga
== 'arty_a7':
695 comb
+= hr
.phy
.rst_n
.eq(ResetSignal())
697 # add blinky lights so we know FPGA is alive
698 if platform
is not None:
699 m
.submodules
.blinky
= Blinky()
701 # connect the arbiter (of wishbone masters)
702 # to the decoder (addressing wishbone slaves)
703 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
705 if hasattr(self
, "cpu"):
706 m
.submodules
.xics_icp
= icp
= self
.xics_icp
707 m
.submodules
.xics_ics
= ics
= self
.xics_ics
708 comb
+= icp
.ics_i
.eq(ics
.icp_o
) # connect ICS to ICP
709 comb
+= self
.cpu
.irq
.eq(icp
.core_irq_o
) # connect ICP to core
711 # wire up the CPU interrupts from the GenericInterrupt
712 comb
+= self
.int_level_i
.eq(self
.intc
.ip
)
715 comb
+= self
.pbus
.stall
.eq(self
.pbus
.cyc
& ~self
.pbus
.ack
)
716 comb
+= self
.sbus
.stall
.eq(self
.sbus
.cyc
& ~self
.sbus
.ack
)
718 # and also wire up make_wb_layout() to wishbone.Interface.
719 # really, XICS_ICS and XICS_ICP both need to be converted
720 # to use wishbone.Interface and this all goes
721 comb
+= icp
.bus
.adr
.eq(self
.pbus
.adr
)
722 comb
+= icp
.bus
.dat_w
.eq(self
.pbus
.dat_w
)
723 comb
+= icp
.bus
.cyc
.eq(self
.pbus
.cyc
)
724 comb
+= icp
.bus
.stb
.eq(self
.pbus
.stb
)
725 comb
+= icp
.bus
.we
.eq(self
.pbus
.we
)
726 comb
+= self
.pbus
.ack
.eq(icp
.bus
.ack
)
727 comb
+= self
.pbus
.dat_r
.eq(icp
.bus
.dat_r
)
728 comb
+= ics
.bus
.adr
.eq(self
.sbus
.adr
)
729 comb
+= ics
.bus
.dat_w
.eq(self
.sbus
.dat_w
)
730 comb
+= ics
.bus
.cyc
.eq(self
.sbus
.cyc
)
731 comb
+= ics
.bus
.stb
.eq(self
.sbus
.stb
)
732 comb
+= ics
.bus
.we
.eq(self
.sbus
.we
)
733 comb
+= self
.sbus
.ack
.eq(ics
.bus
.ack
)
734 comb
+= self
.sbus
.dat_r
.eq(ics
.bus
.dat_r
)
739 # add uart16550 verilog source. assumes a directory
740 # structure where ls2 has been checked out in a common
742 # git clone https://github.com/freecores/uart16550
743 opencores_16550
= "../../uart16550/rtl/verilog"
744 pth
= os
.path
.split(__file__
)[0]
745 pth
= os
.path
.join(pth
, opencores_16550
)
746 fname
= os
.path
.abspath(pth
)
748 self
.uart
.add_verilog_source(fname
, platform
)
750 if hasattr(self
, "spi0"):
752 m
.submodules
.spi0
= spi
= self
.spi0
753 # gonna drive me nuts, this.
754 comb
+= spi
.bus
.stall
.eq(spi
.bus
.cyc
& ~spi
.bus
.ack
)
755 comb
+= spi
.cfg_bus
.stall
.eq(spi
.cfg_bus
.cyc
& ~spi
.cfg_bus
.ack
)
757 # add Tercel verilog source. assumes a directory structure where
758 # microwatt has been checked out in a common subdirectory with:
759 # git clone https://git.libre-soc.org/git/microwatt.git tercel-qspi
760 # git checkout 882ace781e4
761 raptor_tercel
= "../../tercel-qspi/tercel"
762 pth
= os
.path
.split(__file__
)[0]
763 pth
= os
.path
.join(pth
, raptor_tercel
)
764 fname
= os
.path
.abspath(pth
)
766 self
.spi0
.add_verilog_source(fname
, platform
)
768 if hasattr(self
, "eth0"):
769 # add ethernet submodule
770 m
.submodules
.eth0
= ethmac
= self
.eth0
772 # add EthMAC verilog source. assumes a directory
773 # structure where the opencores ethmac has been checked out
774 # in a common subdirectory as:
775 # git clone https://github.com/freecores/ethmac
776 opencores_ethmac
= "../../ethmac/rtl/verilog"
777 pth
= os
.path
.split(__file__
)[0]
778 pth
= os
.path
.join(pth
, opencores_ethmac
)
779 fname
= os
.path
.abspath(pth
)
781 self
.eth0
.add_verilog_source(fname
, platform
)
784 pth
= os
.path
.split(__file__
)[0]
785 pth
= os
.path
.join(pth
, '../external_core_top.v')
786 fname
= os
.path
.abspath(pth
)
787 with
open(fname
) as f
:
788 platform
.add_file(fname
, f
)
793 # puzzlingly the only IO ports needed are peripheral pins,
794 # and at the moment that's just UART tx/rx.
796 ports
+= [self
.uart
.tx_o
, self
.uart
.rx_i
]
797 if hasattr(self
, "hyperram"):
798 ports
+= list(self
.hyperram
.ports())
799 if hasattr(self
, "ddrphy"):
800 if hasattr(self
.ddrphy
, "pads"): # real PHY
801 ports
+= list(self
.ddrphy
.pads
.fields
.values())
802 else: # FakePHY, get at the dfii pads, stops deletion of nets
803 for phase
in self
.dramcore
.dfii
.master
.phases
:
804 print ("dfi master", phase
)
805 ports
+= list(phase
.fields
.values())
806 for phase
in self
.dramcore
.dfii
.slave
.phases
:
807 print ("dfi master", phase
)
808 ports
+= list(phase
.fields
.values())
809 for phase
in self
.dramcore
.dfii
._inti
.phases
:
810 print ("dfi master", phase
)
811 ports
+= list(phase
.fields
.values())
812 ports
+= [ClockSignal(), ResetSignal()]
815 def build_platform(fpga
, firmware
):
817 # create a platform selected from the toolchain.
818 platform_kls
= {'versa_ecp5': VersaECP5Platform
,
819 'versa_ecp5_85': VersaECP5Platform85
,
820 'ulx3s': ULX3S_85F_Platform
,
821 'orangecrab': OrangeCrabR0_2_85k_Platform
,
822 'arty_a7': ArtyA7_100Platform
,
823 'isim': IcarusVersaPlatform
,
826 toolchain
= {'arty_a7': "yosys_nextpnr",
827 'versa_ecp5': 'Trellis',
828 'versa_ecp5_85': 'Trellis',
829 'orangecrab': 'Trellis',
834 dram_cls
= {'arty_a7': None,
835 'versa_ecp5': MT41K64M16
,
836 'versa_ecp5_85': MT41K64M16
,
837 #'versa_ecp5': MT41K256M16,
842 if platform_kls
is not None:
843 platform
= platform_kls(toolchain
=toolchain
)
844 if fpga
== 'versa_ecp5_85':
845 platform
.speed
= "7" # HACK. speed grade 7, sigh
849 print ("platform", fpga
, firmware
, platform
)
851 # set clock frequency
856 dram_clk_freq
= clk_freq
858 clk_freq
= 50e6
# below 50 mhz, stops DRAM being enabled
859 #dram_clk_freq = clk_freq
860 dram_clk_freq
= 100e6
861 if fpga
== 'versa_ecp5':
862 clk_freq
= 50e6
# crank right down to timing threshold
863 #dram_clk_freq = 55e6
864 if fpga
== 'versa_ecp5_85':
865 # 50MHz works. 100MHz works. 55MHz does NOT work.
866 # Stick with multiples of 50MHz...
868 dram_clk_freq
= 100e6
869 if fpga
== 'arty_a7':
873 if fpga
== 'orangecrab':
876 # merge dram_clk_freq with clk_freq if the same
877 if clk_freq
== dram_clk_freq
:
880 # see if dram can be enabled
882 if dram_clk_freq
is not None and dram_clk_freq
>= 50e6
:
884 if dram_clk_freq
is None and clk_freq
>= 50e6
:
887 # select a firmware address
889 if firmware
is not None:
890 fw_addr
= 0xff00_0000 # firmware at HI address, now
892 print ("fpga", fpga
, "firmware", firmware
)
894 # get UART resource pins
895 if platform
is not None:
896 if fpga
=="orangecrab":
897 # assumes an FT232 USB-UART soldered onto these two pins.
898 orangecrab_uart
= UARTResource(0, rx
="N17", tx
="M18")
899 platform
.add_resources([orangecrab_uart
])
901 uart_pins
= platform
.request("uart", 0)
903 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
905 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
907 if (enable_dram
and platform
is not None and
908 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']): # not yet 'arty_a7',
909 ddr_pins
= platform
.request("ddr3", 0,
910 dir={"dq":"-", "dqs":"-"},
911 xdr
={"rst": 4, "clk":4, "a":4,
913 "odt":4, "ras":4, "cas":4, "we":4,
915 print ("ddr pins", ddr_pins
)
917 # Get SPI resource pins
919 if False and platform
is not None and \
920 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
921 # Override here to get FlashResource out of the way and enable Tercel
922 # direct access to the SPI flash.
923 # each pin needs a separate direction control
926 Subsignal("dq0", Pins("W2", dir="io")),
927 Subsignal("dq1", Pins("V2", dir="io")),
928 Subsignal("dq2", Pins("Y2", dir="io")),
929 Subsignal("dq3", Pins("W1", dir="io")),
930 Subsignal("cs_n", Pins("R2", dir="o")),
931 Attrs(PULLMODE
="NONE", DRIVE
="4", IO_TYPE
="LVCMOS33"))
933 platform
.add_resources(spi_0_ios
)
934 spi_0_pins
= platform
.request("spi_0", 0, dir={"cs_n":"o"},
935 xdr
={"dq0":1, "dq1": 1,
939 if platform
is not None and \
941 # each pin needs a separate direction control
944 Subsignal("dq0", Pins("K17", dir="io")),
945 Subsignal("dq1", Pins("K18", dir="io")),
946 Subsignal("dq2", Pins("L14", dir="io")),
947 Subsignal("dq3", Pins("M14", dir="io")),
948 Subsignal("cs_n", Pins("L13", dir="o")),
949 Subsignal("clk", Pins("L16", dir="o")),
950 Attrs(PULLMODE
="NONE", DRIVE
="4", IO_TYPE
="LVCMOS33"))
952 platform
.add_resources(spi_0_ios
)
953 spi_0_pins
= platform
.request("spi_0", 0)
955 print ("spiflash pins", spi_0_pins
)
957 # Get Ethernet RMII resource pins
959 if False and platform
is not None and \
960 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
961 # Mainly on X3 connector, MDIO on X4 due to lack of pins
963 Resource("ethmac_0", 0,
964 Subsignal("mtx_clk", Pins("B19", dir="i")),
965 Subsignal("mtxd", Pins("B12 B9 E6 D6", dir="o")),
966 Subsignal("mtxen", Pins("E7", dir="o")),
967 Subsignal("mtxerr", Pins("D7", dir="o")),
968 Subsignal("mrx_clk", Pins("B11", dir="i")),
969 Subsignal("mrxd", Pins("B6 E9 D9 B8", dir="i")),
970 Subsignal("mrxdv", Pins("C8", dir="i")),
971 Subsignal("mrxerr", Pins("D8", dir="i")),
972 Subsignal("mcoll", Pins("E8", dir="i")),
973 Subsignal("mcrs", Pins("C7", dir="i")),
974 Subsignal("mdc", Pins("B18", dir="o")),
975 Subsignal("md", Pins("A18", dir="io")),
976 Attrs(PULLMODE
="NONE", DRIVE
="8", SLEWRATE
="FAST",
979 platform
.add_resources(ethmac_0_ios
)
980 ethmac_0_pins
= platform
.request("ethmac_0", 0,
981 dir={"mtx_clk":"i", "mtxd":"o",
983 "mtxerr":"o", "mrx_clk":"i",
985 "mrxdv":"i", "mrxerr":"i",
987 "mcrs":"i", "mdc":"o", "md":"io"},
988 xdr
={"mtx_clk": 0, "mtxd": 0,
990 "mtxerr": 0, "mrx_clk": 0,
992 "mrxdv": 0, "mrxerr": 0,
994 "mcrs": 0, "mdc": 0, "md": 0})
995 print ("ethmac pins", ethmac_0_pins
)
1000 hyperram_pins
= [HyperRAMPads()]
1001 elif fpga
in ['isim']:
1002 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
1003 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
1004 rwds
="C14", rst_n
="E13", ck_p
="D13",
1005 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
1006 platform
.add_resources(hyperram_ios
)
1007 hyperram_pins
= [platform
.request("hyperram")]
1008 print ("isim a7 hyperram", hyperram_ios
)
1009 # Digilent Arty A7-100t
1010 elif platform
is not None and fpga
in ['arty_a7']:
1011 hyperram_ios
= HyperRAMResource(0, cs_n
="B11 B18 G13 D13",
1012 dq
="E15 E16 D15 C15 J15 K15 J18 J17",
1013 rwds
="K16", rst_n
="A18", ck_p
="A11",
1014 # ck_n="D12" - for later (DDR)
1015 attrs
=Attrs(IOSTANDARD
="LVCMOS33"))
1016 platform
.add_resources(hyperram_ios
)
1017 hyperram_ios
= HyperRAMResource(1, cs_n
="V12 V14 U12 U14",
1018 dq
="D4 D3 F4 F3 G2 H2 D2 E2",
1019 rwds
="U13", rst_n
="T13", ck_p
="V10",
1020 # ck_n="V11" - for later (DDR)
1021 attrs
=Attrs(IOSTANDARD
="LVCMOS33"))
1022 platform
.add_resources(hyperram_ios
)
1023 hyperram_pins
= [platform
.request("hyperram", 0),
1024 platform
.request("hyperram", 1)]
1025 print ("arty a7 hyperram", hyperram_ios
)
1027 elif False and platform
is not None and fpga
in \
1028 ['versa_ecp5', 'versa_ecp5_85']:
1029 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
1030 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
1031 rwds
="C14", rst_n
="E13", ck_p
="D13",
1032 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
1033 platform
.add_resources(hyperram_ios
)
1034 hyperram_pins
= [platform
.request("hyperram")]
1035 print ("versa ecp5 hyperram", hyperram_ios
)
1036 print ("hyperram pins", hyperram_pins
)
1039 soc
= DDR3SoC(fpga
=fpga
, dram_cls
=dram_cls
,
1040 # check microwatt_soc.h for these
1041 ddrphy_addr
=0xfff00000, # DRAM_INIT_BASE, PHY address
1042 dramcore_addr
=0xc8000000, # DRAM_CTRL_BASE
1043 ddr_addr
=0x00000000, # DRAM_BASE
1044 spi0_addr
=0xf0000000, # SPI0_BASE
1045 spi0_cfg_addr
=0xc0006000, # SPI0_CTRL_BASE
1046 eth0_cfg_addr
=0xc000c000, # ETH0_CTRL_BASE (4k)
1047 eth0_irqno
=1, # ETH0_IRQ number (match microwatt)
1048 hyperram_addr
=[0xa0000000, # HYPERRAM_BASE1
1049 0xa2000000], # HYPERRAM_BASE2
1053 uart_pins
=uart_pins
,
1054 uart_irqno
=0, # UART_IRQ number (match microwatt)
1055 uart_addr
=0xc0002000, # UART0_ADDR
1056 spi_0_pins
=spi_0_pins
,
1057 ethmac_0_pins
=ethmac_0_pins
,
1058 hyperram_pins
=hyperram_pins
,
1060 xics_icp_addr
=0xc000_4000, # XICS_ICP_BASE
1061 xics_ics_addr
=0xc000_5000, # XICS_ICS_BASE
1063 dram_clk_freq
=dram_clk_freq
,
1066 if toolchain
== 'Trellis':
1067 # add -abc9 option to yosys synth_ecp5
1068 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
1069 #os.environ['NMIGEN_synth_opts'] = '-abc9'
1070 os
.environ
['NMIGEN_synth_opts'] = '-nowidelut'
1072 if platform
is not None:
1073 # build and upload it
1075 platform
.build(soc
, do_program
=False,
1076 do_build
=True, build_dir
="build_simsoc")
1078 platform
.build(soc
, do_program
=True)
1080 # for now, generate verilog
1081 vl
= verilog
.convert(soc
, ports
=soc
.ports())
1082 with
open("ls2.v", "w") as f
:
1086 # urrr this gets exec()d by the build process without arguments
1087 # which screws up. use the arty_a7_ls2.py etc. with no arguments
1088 if __name__
== '__main__':
1091 if len(sys
.argv
) >= 2:
1093 if len(sys
.argv
) >= 3:
1094 firmware
= sys
.argv
[2]
1095 build_platform(fpga
, firmware
)