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
)
13 from nmigen
.build
.dsl
import Attrs
14 from nmigen
.cli
import verilog
15 from nmigen
.lib
.cdc
import ResetSynchronizer
16 from nmigen_soc
import wishbone
, memory
17 from nmigen_soc
.memory
import MemoryMap
18 from nmigen
.utils
import log2_int
20 from nmigen_stdio
.serial
import AsyncSerial
23 from nmigen_boards
.resources
.memory
import HyperRAMResource
24 from lambdasoc
.periph
.hyperram
import HyperRAM
, HyperRAMPads
, HyperRAMPHY
26 from lambdasoc
.periph
.event
import IRQLine
27 from lambdasoc
.periph
.intc
import GenericInterruptController
28 from lambdasoc
.periph
.sram
import SRAMPeripheral
29 from lambdasoc
.periph
.timer
import TimerPeripheral
30 from lambdasoc
.periph
import Peripheral
31 from lambdasoc
.soc
.base
import SoC
32 from soc
.bus
.uart_16550
import UART16550
# opencores 16550 uart
33 from soc
.bus
.tercel
import Tercel
# SPI XIP master
34 from soc
.bus
.opencores_ethmac
import EthMAC
# OpenCores 10/100 Ethernet MAC
35 from soc
.bus
.external_core
import ExternalCore
# external libresoc/microwatt
36 from soc
.bus
.wb_downconvert
import WishboneDownConvert
37 from soc
.bus
.syscon
import MicrowattSYSCON
38 from soc
.interrupts
.xics
import XICS_ICP
, XICS_ICS
41 from gram
.common
import (PhySettings
, get_cl_cw
, get_sys_latency
,
43 from gram
.core
import gramCore
44 from gram
.phy
.ecp5ddrphy
import ECP5DDRPHY
45 from gram
.phy
.fakephy
import FakePHY
, SDRAM_VERBOSE_STD
, SDRAM_VERBOSE_DBG
46 from gram
.modules
import MT41K256M16
, MT41K64M16
47 from gram
.frontend
.wishbone
import gramWishbone
50 from nmigen
.build
import Resource
51 from nmigen
.build
import Subsignal
52 from nmigen
.build
import Pins
54 # Board (and simulation) platforms
55 from nmigen_boards
.versa_ecp5
import VersaECP5Platform
56 from nmigen_boards
.versa_ecp5
import VersaECP5Platform85
# custom board
57 from nmigen_boards
.ulx3s
import ULX3S_85F_Platform
58 from nmigen_boards
.arty_a7
import ArtyA7_100Platform
59 from nmigen_boards
.test
.blinky
import Blinky
60 from icarusversa
import IcarusVersaPlatform
61 # Clock-Reset Generator (works for all ECP5 platforms)
62 from ecp5_crg
import ECP5CRG
63 from arty_crg
import ArtyA7CRG
68 def sim_ddr3_settings(clk_freq
=100e6
):
69 tck
= 2/(2*2*clk_freq
)
75 cl
, cwl
= get_cl_cw("DDR3", tck
)
76 cl_sys_latency
= get_sys_latency(nphases
, cl
)
77 cwl_sys_latency
= get_sys_latency(nphases
, cwl
)
78 rdcmdphase
, rdphase
= get_sys_phases(nphases
, cl_sys_latency
, cl
)
79 wrcmdphase
, wrphase
= get_sys_phases(nphases
, cwl_sys_latency
, cwl
)
84 dfi_databits
=4*databits
,
89 rdcmdphase
=rdcmdphase
,
90 wrcmdphase
=wrcmdphase
,
93 read_latency
=2 + cl_sys_latency
+ 2 + log2_int(4//nphases
) + 4,
94 write_latency
=cwl_sys_latency
98 class WB64to32Convert(Elaboratable
):
99 """Microwatt IO wishbone slave 64->32 bits converter
101 For timing reasons, this adds a one cycle latch on the way both
102 in and out. This relaxes timing and routing pressure on the "main"
103 memory bus by moving all simple IOs to a slower 32-bit bus.
105 This implementation is rather dumb at the moment, no stash buffer,
106 so we stall whenever that latch is busy. This can be improved.
108 def __init__(self
, master
, slave
):
112 def elaborate(self
, platform
):
114 comb
, sync
= m
.d
.comb
, m
.d
.sync
115 master
, slave
= self
.master
, self
.slave
122 with m
.State("IDLE"):
123 # Clear ACK (and has_top_r) in case it was set
124 sync
+= master
.ack
.eq(0)
125 sync
+= has_top_r
.eq(0)
127 # Do we have a cycle ?
128 with m
.If(master
.cyc
& master
.stb
):
129 # Stall master until we are done, we are't (yet) pipelining
130 # this, it's all slow IOs.
131 sync
+= master
.stall
.eq(1)
133 # Start cycle downstream
134 sync
+= slave
.cyc
.eq(1)
135 sync
+= slave
.stb
.eq(1)
137 # Do we have a top word and/or a bottom word ?
138 comb
+= has_top
.eq(master
.sel
[4:].bool())
139 comb
+= has_bot
.eq(master
.sel
[:4].bool())
140 # record the has_top flag for the next FSM state
141 sync
+= has_top_r
.eq(has_top
)
143 # Copy write enable to IO out, copy address as well,
144 # LSB is set later based on HI/LO
145 sync
+= slave
.we
.eq(master
.we
)
146 sync
+= slave
.adr
.eq(Cat(0, master
.adr
))
148 # If we have a bottom word, handle it first, otherwise
149 # send the top word down. XXX Split the actual mux out
150 # and only generate a control signal.
152 with m
.If(master
.we
):
153 sync
+= slave
.dat_w
.eq(master
.dat_w
[:32])
154 sync
+= slave
.sel
.eq(master
.sel
[:4])
156 # Wait for ack on BOTTOM half
157 m
.next
= "WAIT_ACK_BOT"
160 with m
.If(master
.we
):
161 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
162 sync
+= slave
.sel
.eq(master
.sel
[4:])
164 # Bump LSB of address
165 sync
+= slave
.adr
[0].eq(1)
167 # Wait for ack on TOP half
168 m
.next
= "WAIT_ACK_TOP"
171 with m
.State("WAIT_ACK_BOT"):
172 # If we aren't stalled by the device, clear stb
173 if hasattr(slave
, "stall"):
174 with m
.If(~slave
.stall
):
175 sync
+= slave
.stb
.eq(0)
178 with m
.If(slave
.ack
):
179 # If it's a read, latch the data
180 with m
.If(~slave
.we
):
181 sync
+= master
.dat_r
[:32].eq(slave
.dat_r
)
183 # Do we have a "top" part as well ?
184 with m
.If(has_top_r
):
186 with m
.If(master
.we
):
187 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
188 sync
+= slave
.sel
.eq(master
.sel
[4:])
190 # Bump address and set STB
191 sync
+= slave
.adr
[0].eq(1)
192 sync
+= slave
.stb
.eq(1)
195 m
.next
= "WAIT_ACK_TOP"
198 # We are done, ack up, clear cyc downstram
199 sync
+= slave
.cyc
.eq(0)
200 sync
+= slave
.stb
.eq(0)
202 # And ack & unstall upstream
203 sync
+= master
.ack
.eq(1)
204 if hasattr(master
, "stall"):
205 sync
+= master
.stall
.eq(0)
210 with m
.State("WAIT_ACK_TOP"):
211 # If we aren't stalled by the device, clear stb
212 if hasattr(slave
, "stall"):
213 with m
.If(~slave
.stall
):
214 sync
+= slave
.stb
.eq(0)
217 with m
.If(slave
.ack
):
218 # If it's a read, latch the data
219 with m
.If(~slave
.we
):
220 sync
+= master
.dat_r
[32:].eq(slave
.dat_r
)
222 # We are done, ack up, clear cyc downstram
223 sync
+= slave
.cyc
.eq(0)
224 sync
+= slave
.stb
.eq(0)
226 # And ack & unstall upstream
227 sync
+= master
.ack
.eq(1)
228 if hasattr(master
, "stall"):
229 sync
+= master
.stall
.eq(0)
237 class DDR3SoC(SoC
, Elaboratable
):
238 def __init__(self
, *,
241 uart_pins
=None, spi_0_pins
=None, ethmac_0_pins
=None,
242 ddr_pins
=None, ddrphy_addr
=None,
243 dramcore_addr
=None, ddr_addr
=None,
244 fw_addr
=0x0000_0000, firmware
=None,
245 uart_addr
=None, uart_irqno
=0,
246 spi0_addr
=None, spi0_cfg_addr
=None,
247 eth0_cfg_addr
=None, eth0_irqno
=None,
250 xics_icp_addr
=None, xics_ics_addr
=None,
254 # wishbone routing is as follows:
265 # arbiter------------------------------------------+
267 # +---decoder----+--------+---------+-------+--------+ |
269 # uart XICS CSRs DRAM XIP SPI HyperRAM EthMAC
271 # set up wishbone bus arbiter and decoder. arbiter routes,
272 # decoder maps local-relative addressed satellites to global addresses
273 self
._arbiter
= wishbone
.Arbiter(addr_width
=30, data_width
=32,
275 features
={"cti", "bte", "stall"})
276 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32,
278 features
={"cti", "bte", "stall"})
280 # default firmware name
282 firmware
= "firmware/main.bin"
284 # set up clock request generator
286 if fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s']:
289 self
.crg
= ECP5CRG(clk_freq
, dram_clk_freq
=None, pod_bits
=pod_bits
)
290 if fpga
in ['arty_a7']:
291 self
.crg
= ArtyA7CRG(clk_freq
)
293 # set up CPU, with 64-to-32-bit downconverters
295 self
.cpu
= ExternalCore(name
="ext_core")
296 cvtdbus
= wishbone
.Interface(addr_width
=30, data_width
=32,
297 granularity
=8, features
={'stall'})
298 cvtibus
= wishbone
.Interface(addr_width
=30, data_width
=32,
299 granularity
=8, features
={'stall'})
300 self
.dbusdowncvt
= WB64to32Convert(self
.cpu
.dbus
, cvtdbus
)
301 self
.ibusdowncvt
= WB64to32Convert(self
.cpu
.ibus
, cvtibus
)
302 self
._arbiter
.add(cvtibus
) # I-Cache Master
303 self
._arbiter
.add(cvtdbus
) # D-Cache Master. TODO JTAG master
304 self
.cvtibus
= cvtibus
305 self
.cvtdbus
= cvtdbus
307 # CPU interrupt controller, needs stall to be added, also
308 # compat with wishbone.Interface
309 self
.intc
= GenericInterruptController(width
=len(self
.cpu
.irq
))
310 self
.xics_icp
= icp
= XICS_ICP()
311 self
.xics_ics
= ics
= XICS_ICS()
312 self
.int_level_i
= self
.xics_ics
.int_level_i
314 self
.pbus
= pbus
= wishbone
.Interface(name
="xics_icp_bus",
315 addr_width
=10, data_width
=32,
316 granularity
=8, features
={'stall'})
317 self
.sbus
= sbus
= wishbone
.Interface(name
="xics_ics_bus",
318 addr_width
=10, data_width
=32,
319 granularity
=8, features
={'stall'})
320 pmap
= MemoryMap(addr_width
=12, data_width
=8, name
="icp_map")
321 pbus
.memory_map
= pmap
322 self
._decoder
.add(pbus
, addr
=xics_icp_addr
) # ICP addr
324 smap
= MemoryMap(addr_width
=12, data_width
=8, name
="ics_map")
325 sbus
.memory_map
= smap
326 self
._decoder
.add(sbus
, addr
=xics_ics_addr
) # ICP addr
329 # SRAM (but actually a ROM, for firmware)
330 if fw_addr
is not None:
331 print ("fw at address %x" % fw_addr
)
333 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
335 if firmware
is not None:
336 with
open(firmware
, "rb") as f
:
337 words
= iter(lambda: f
.read(sram_width
// 8), b
'')
338 bios
= [int.from_bytes(w
, "little") for w
in words
]
339 self
.bootmem
.init
= bios
340 self
._decoder
.add(self
.bootmem
.bus
, addr
=fw_addr
) # ROM at fw_addr
342 # System Configuration info
343 # offset executable ELF payload at 6 megabyte offset (2<<20)
344 spi_offset
= 2<<20 if (spi_0_pins
is not None) else None
345 dram_offset
= ddr_addr
if (ddr_pins
is not None) else None
346 self
.syscon
= MicrowattSYSCON(sys_clk_freq
=clk_freq
,
347 has_uart
=(uart_pins
is not None),
348 spi_offset
=spi_offset
,
349 dram_addr
=dram_offset
)
350 self
._decoder
.add(self
.syscon
.bus
, addr
=0xc0000000) # at 0xc000_0000
353 # SRAM (read-writeable BRAM)
354 self
.ram
= SRAMPeripheral(size
=4096)
355 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
357 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
358 if uart_pins
is not None:
359 # sigh actual UART in microwatt is 8-bit
360 self
.uart_irq
= IRQLine()
361 self
.uart
= UART16550(data_width
=8, pins
=uart_pins
,
364 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
365 # strictly speaking this is a nmigen-soc "sparse" arrangement
366 # which should be handled by MemoryMap, but needs investigation
367 cvtuartbus
= wishbone
.Interface(addr_width
=5, data_width
=32,
370 umap
= MemoryMap(addr_width
=7, data_width
=8, name
="uart_map")
371 cvtuartbus
.memory_map
= umap
372 self
._decoder
.add(cvtuartbus
, addr
=uart_addr
) # 16550 UART addr
373 self
.cvtuartbus
= cvtuartbus
374 self
.intc
.add_irq(self
.uart
.irq
, index
=uart_irqno
)
376 # SDRAM module using opencores sdr_ctrl
378 class MT48LC16M16(SDRModule):
384 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
388 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
397 if ddr_pins
is not None: # or fpga == 'sim':
398 ddrmodule
= dram_cls(clk_freq
, "1:2") # match DDR3 ASIC P/N
400 # remap both the sync domain (wherever it occurs) and
401 # the sync2x domain. technically this should NOT be done.
402 # it's a bit of a mess. ok: this should be done only
403 # when dramsync===sync (and dramsync2x===sync2x)
404 drs
= DomainRenamer({"sync": "dramsync",
405 "sync2x": "dramsync2x"})
407 # HOWEVER, when the ASyncBridge is deployed, the two domains
408 # must NOT be renamed, instead this used:
410 # and then the ASyncBridge takes care of the two.
411 # but, back in ecp5_crg.py, when ASyncBridge is added,
412 # dram_clk_freq must be passed to ECP5CRG, which will call
413 # ECP5CRG.phase2_domain on your behalf, setting up the
414 # necessary dramsync2x which is needed for the xdr=4 IOpads
417 self
.ddrphy
= FakePHY(module
=ddrmodule
,
418 settings
=sim_ddr3_settings(clk_freq
),
419 verbosity
=SDRAM_VERBOSE_DBG
,
422 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
, sys_clk_freq
=clk_freq
))
423 self
._decoder
.add(self
.ddrphy
.bus
, addr
=ddrphy_addr
)
425 dramcore
= gramCore(phy
=self
.ddrphy
,
426 geom_settings
=ddrmodule
.geom_settings
,
427 timing_settings
=ddrmodule
.timing_settings
,
430 self
.dramcore
= dramcore
432 self
.dramcore
= drs(dramcore
)
433 self
._decoder
.add(self
.dramcore
.bus
, addr
=dramcore_addr
)
435 # map the DRAM onto Wishbone, XXX use stall but set classic below
436 # XXX WHEN ADDING ASYNCBRIDGE IT IS THE **BRIDGE** THAT MUST
437 # XXX HAVE THE STALL SIGNAL, AND THE **BRIDGE** THAT MUST HAVE
438 # XXX stall=stb&~ack APPLIED
439 drambone
= gramWishbone(dramcore
, features
={'stall'})
441 self
.drambone
= drambone
443 self
.drambone
= drs(drambone
)
444 # XXX ADD THE ASYNCBRIDGE NOT THE DRAMBONE.BUS, THEN
445 # XXX ADD DRAMBONE.BUS TO ASYNCBRIDGE
446 self
._decoder
.add(self
.drambone
.bus
, addr
=ddr_addr
)
448 # additional SRAM at address if DRAM is not also at 0x0
449 # (TODO, check Flash, and HyperRAM as well)
450 if (ddr_pins
is None or ddr_addr
!= 0x0) and fw_addr
!= 0:
451 print ("SRAM 0x8000 at address 0x0")
453 self
.sram
= SRAMPeripheral(size
=0x8000,
454 data_width
=sram_width
,
456 self
._decoder
.add(self
.sram
.bus
, addr
=0x0) # RAM at 0x0
459 if spi_0_pins
is not None and fpga
in ['sim',
461 'rcs_arctic_tern_bmc_card',
465 # The Lattice ECP5 devices require special handling on the
466 # dedicated SPI clock line, which is shared with the internal
467 # SPI controller used for FPGA bitstream loading.
468 spi0_is_lattice_ecp5_clk
= False
469 if fpga
in ['versa_ecp5',
471 'rcs_arctic_tern_bmc_card',
473 spi0_is_lattice_ecp5_clk
= True
475 # Tercel contains two independent Wishbone regions, a
476 # configuration region and the direct API access region,
477 # Set the SPI 0 access region to 16MB, as the FPGA
478 # bitstream Flash device is unlikely to be larger than this.
479 # The main SPI Flash (SPI 1) should be set to at
480 # least 28 bits (256MB) to allow the use of large 4BA devices.
481 self
.spi0
= Tercel(data_width
=32, spi_region_addr_width
=24,
482 adr_offset
=spi0_addr
,
486 lattice_ecp5_usrmclk
=spi0_is_lattice_ecp5_clk
)
487 self
._decoder
.add(self
.spi0
.bus
, addr
=spi0_addr
)
488 self
._decoder
.add(self
.spi0
.cfg_bus
, addr
=spi0_cfg_addr
)
491 if ethmac_0_pins
is not None and fpga
in ['versa_ecp5',
494 self
.eth_irq
= IRQLine()
495 # The OpenCores Ethernet MAC contains two independent Wishbone
496 # interfaces, a slave (configuration) interface and a master (DMA)
498 self
.eth0
= EthMAC(pins
=ethmac_0_pins
, irq
=self
.eth_irq
)
499 self
._arbiter
.add(self
.eth0
.master_bus
)
500 self
._decoder
.add(self
.eth0
.slave_bus
, addr
=eth0_cfg_addr
)
501 self
.intc
.add_irq(self
.eth0
.irq
, index
=eth0_irqno
)
503 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
504 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
506 if hyperram_pins
is not None:
507 self
.hyperram
= HyperRAM(io
=hyperram_pins
, phy_kls
=HyperRAMPHY
,
509 latency
=7) # Winbond W956D8MBYA
510 self
._decoder
.add(self
.hyperram
.bus
, addr
=hyperram_addr
)
512 self
.memory_map
= self
._decoder
.bus
.memory_map
514 self
.clk_freq
= clk_freq
517 def elaborate(self
, platform
):
521 # add the peripherals and clock-reset-generator
522 if platform
is not None and hasattr(self
, "crg"):
523 m
.submodules
.sysclk
= self
.crg
525 if hasattr(self
, "sram"):
526 m
.submodules
.sram
= self
.sram
527 if hasattr(self
, "bootmem"):
528 m
.submodules
.bootmem
= self
.bootmem
529 m
.submodules
.syscon
= self
.syscon
530 if hasattr(self
, "ram"):
531 m
.submodules
.ram
= self
.ram
532 if hasattr(self
, "uart"):
533 m
.submodules
.uart
= self
.uart
534 comb
+= self
.uart
.cts_i
.eq(1)
535 comb
+= self
.uart
.dsr_i
.eq(1)
536 comb
+= self
.uart
.ri_i
.eq(0)
537 comb
+= self
.uart
.dcd_i
.eq(1)
538 # sigh connect up the wishbone bus manually to deal with
539 # the mis-match on the data. nmigen-soc "sparse" MemoryMap
540 # should be able to deal with this. TODO, investigate
541 uartbus
= self
.uart
.bus
542 comb
+= uartbus
.adr
.eq(self
.cvtuartbus
.adr
)
543 comb
+= uartbus
.stb
.eq(self
.cvtuartbus
.stb
)
544 comb
+= uartbus
.cyc
.eq(self
.cvtuartbus
.cyc
)
545 comb
+= uartbus
.sel
.eq(self
.cvtuartbus
.sel
)
546 comb
+= uartbus
.we
.eq(self
.cvtuartbus
.we
)
547 comb
+= uartbus
.dat_w
.eq(self
.cvtuartbus
.dat_w
) # drops 8..31
548 comb
+= self
.cvtuartbus
.dat_r
.eq(uartbus
.dat_r
) # drops 8..31
549 comb
+= self
.cvtuartbus
.ack
.eq(uartbus
.ack
)
550 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
551 comb
+= uartbus
.stall
.eq(uartbus
.cyc
& ~uartbus
.ack
)
552 comb
+= self
.cvtuartbus
.stall
.eq(uartbus
.stall
)
553 if hasattr(self
, "cpu"):
554 m
.submodules
.intc
= self
.intc
555 m
.submodules
.extcore
= self
.cpu
556 m
.submodules
.dbuscvt
= self
.dbusdowncvt
557 m
.submodules
.ibuscvt
= self
.ibusdowncvt
558 # create stall sigs, assume wishbone classic
559 #ibus, dbus = self.cvtibus, self.cvtdbus
560 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
561 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
563 m
.submodules
.arbiter
= self
._arbiter
564 m
.submodules
.decoder
= self
._decoder
565 if hasattr(self
, "ddrphy"):
566 m
.submodules
.ddrphy
= self
.ddrphy
567 m
.submodules
.dramcore
= self
.dramcore
568 m
.submodules
.drambone
= drambone
= self
.drambone
569 # grrr, same problem with drambone: not WB4-pipe compliant
570 # XXX TAKE THIS OUT, REPLACE WITH ASYNCBRIDGE HAVING
571 # XXX asyncbridge.bus.stall.eq(asyncbridge.bus.cyc & ...)
572 comb
+= drambone
.bus
.stall
.eq(drambone
.bus
.cyc
& ~drambone
.bus
.ack
)
574 # add hyperram module
575 if hasattr(self
, "hyperram"):
576 m
.submodules
.hyperram
= hyperram
= self
.hyperram
577 # grrr, same problem with hyperram: not WB4-pipe compliant
578 comb
+= hyperram
.bus
.stall
.eq(hyperram
.bus
.cyc
& ~hyperram
.bus
.ack
)
579 # set 3 top CSn lines to zero for now
580 if self
.fpga
== 'arty_a7':
581 comb
+= hyperram
.phy
.rst_n
.eq(ResetSignal())
583 # add blinky lights so we know FPGA is alive
584 if platform
is not None:
585 m
.submodules
.blinky
= Blinky()
587 # connect the arbiter (of wishbone masters)
588 # to the decoder (addressing wishbone slaves)
589 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
591 if hasattr(self
, "cpu"):
592 m
.submodules
.xics_icp
= icp
= self
.xics_icp
593 m
.submodules
.xics_ics
= ics
= self
.xics_ics
594 comb
+= icp
.ics_i
.eq(ics
.icp_o
) # connect ICS to ICP
595 comb
+= self
.cpu
.irq
.eq(icp
.core_irq_o
) # connect ICP to core
597 # wire up the CPU interrupts from the GenericInterrupt
598 comb
+= self
.int_level_i
.eq(self
.intc
.ip
)
601 comb
+= self
.pbus
.stall
.eq(self
.pbus
.cyc
& ~self
.pbus
.ack
)
602 comb
+= self
.sbus
.stall
.eq(self
.sbus
.cyc
& ~self
.sbus
.ack
)
604 # and also wire up make_wb_layout() to wishbone.Interface.
605 # really, XICS_ICS and XICS_ICP both need to be converted
606 # to use wishbone.Interface and this all goes
607 comb
+= icp
.bus
.adr
.eq(self
.pbus
.adr
)
608 comb
+= icp
.bus
.dat_w
.eq(self
.pbus
.dat_w
)
609 comb
+= icp
.bus
.cyc
.eq(self
.pbus
.cyc
)
610 comb
+= icp
.bus
.stb
.eq(self
.pbus
.stb
)
611 comb
+= icp
.bus
.we
.eq(self
.pbus
.we
)
612 comb
+= self
.pbus
.ack
.eq(icp
.bus
.ack
)
613 comb
+= self
.pbus
.dat_r
.eq(icp
.bus
.dat_r
)
614 comb
+= ics
.bus
.adr
.eq(self
.sbus
.adr
)
615 comb
+= ics
.bus
.dat_w
.eq(self
.sbus
.dat_w
)
616 comb
+= ics
.bus
.cyc
.eq(self
.sbus
.cyc
)
617 comb
+= ics
.bus
.stb
.eq(self
.sbus
.stb
)
618 comb
+= ics
.bus
.we
.eq(self
.sbus
.we
)
619 comb
+= self
.sbus
.ack
.eq(ics
.bus
.ack
)
620 comb
+= self
.sbus
.dat_r
.eq(ics
.bus
.dat_r
)
625 # add uart16550 verilog source. assumes a directory
626 # structure where ls2 has been checked out in a common
628 # git clone https://github.com/freecores/uart16550
629 opencores_16550
= "../../uart16550/rtl/verilog"
630 pth
= os
.path
.split(__file__
)[0]
631 pth
= os
.path
.join(pth
, opencores_16550
)
632 fname
= os
.path
.abspath(pth
)
634 self
.uart
.add_verilog_source(fname
, platform
)
636 if hasattr(self
, "spi0"):
638 m
.submodules
.spi0
= spi
= self
.spi0
639 # gonna drive me nuts, this.
640 comb
+= spi
.bus
.stall
.eq(spi
.bus
.cyc
& ~spi
.bus
.ack
)
641 comb
+= spi
.cfg_bus
.stall
.eq(spi
.cfg_bus
.cyc
& ~spi
.cfg_bus
.ack
)
643 # add Tercel verilog source. assumes a directory structure where
644 # microwatt has been checked out in a common subdirectory with:
645 # git clone https://git.libre-soc.org/git/microwatt.git tercel-qspi
646 # git checkout 882ace781e4
647 raptor_tercel
= "../../tercel-qspi/tercel"
648 pth
= os
.path
.split(__file__
)[0]
649 pth
= os
.path
.join(pth
, raptor_tercel
)
650 fname
= os
.path
.abspath(pth
)
652 self
.spi0
.add_verilog_source(fname
, platform
)
654 if hasattr(self
, "eth0"):
655 # add ethernet submodule
656 m
.submodules
.eth0
= ethmac
= self
.eth0
658 # add EthMAC verilog source. assumes a directory
659 # structure where the opencores ethmac has been checked out
660 # in a common subdirectory as:
661 # git clone https://github.com/freecores/ethmac
662 opencores_ethmac
= "../../ethmac/rtl/verilog"
663 pth
= os
.path
.split(__file__
)[0]
664 pth
= os
.path
.join(pth
, opencores_ethmac
)
665 fname
= os
.path
.abspath(pth
)
667 self
.eth0
.add_verilog_source(fname
, platform
)
670 pth
= os
.path
.split(__file__
)[0]
671 pth
= os
.path
.join(pth
, '../external_core_top.v')
672 fname
= os
.path
.abspath(pth
)
673 with
open(fname
) as f
:
674 platform
.add_file(fname
, f
)
679 # puzzlingly the only IO ports needed are peripheral pins,
680 # and at the moment that's just UART tx/rx.
682 ports
+= [self
.uart
.tx_o
, self
.uart
.rx_i
]
683 if hasattr(self
, "hyperram"):
684 ports
+= list(self
.hyperram
.ports())
685 if hasattr(self
, "ddrphy"):
686 if hasattr(self
.ddrphy
, "pads"): # real PHY
687 ports
+= list(self
.ddrphy
.pads
.fields
.values())
688 else: # FakePHY, get at the dfii pads, stops deletion of nets
689 for phase
in self
.dramcore
.dfii
.master
.phases
:
690 print ("dfi master", phase
)
691 ports
+= list(phase
.fields
.values())
692 for phase
in self
.dramcore
.dfii
.slave
.phases
:
693 print ("dfi master", phase
)
694 ports
+= list(phase
.fields
.values())
695 for phase
in self
.dramcore
.dfii
._inti
.phases
:
696 print ("dfi master", phase
)
697 ports
+= list(phase
.fields
.values())
698 ports
+= [ClockSignal(), ResetSignal()]
701 def build_platform(fpga
, firmware
):
703 # create a platform selected from the toolchain.
704 platform_kls
= {'versa_ecp5': VersaECP5Platform
,
705 'versa_ecp5_85': VersaECP5Platform85
,
706 'ulx3s': ULX3S_85F_Platform
,
707 'arty_a7': ArtyA7_100Platform
,
708 'isim': IcarusVersaPlatform
,
711 toolchain
= {'arty_a7': "yosys_nextpnr",
712 'versa_ecp5': 'Trellis',
713 'versa_ecp5_85': 'Trellis',
718 dram_cls
= {'arty_a7': None,
719 'versa_ecp5': MT41K64M16
,
720 'versa_ecp5_85': MT41K64M16
,
721 #'versa_ecp5': MT41K256M16,
726 if platform_kls
is not None:
727 platform
= platform_kls(toolchain
=toolchain
)
728 if fpga
== 'versa_ecp5_85':
729 platform
.speed
= "7" # HACK. speed grade 7, sigh
733 print ("platform", fpga
, firmware
, platform
)
735 # set clock frequency
740 clk_freq
= 55e6
# below 50 mhz, stops DRAM being enabled
741 if fpga
== 'versa_ecp5':
742 clk_freq
= 55e6
# crank right down to test hyperram
743 if fpga
== 'versa_ecp5_85':
744 # 50MHz works. 100MHz works. 55MHz does NOT work.
745 # Stick with multiples of 50MHz...
747 if fpga
== 'arty_a7':
752 # select a firmware address
754 if firmware
is not None:
755 fw_addr
= 0xff00_0000 # firmware at HI address, now
757 print ("fpga", fpga
, "firmware", firmware
)
759 # get UART resource pins
760 if platform
is not None:
761 uart_pins
= platform
.request("uart", 0)
763 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
765 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
767 if (clk_freq
>= 50e6
and platform
is not None and
768 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
769 ddr_pins
= platform
.request("ddr3", 0,
770 dir={"dq":"-", "dqs":"-"},
771 xdr
={"rst": 1, "clk":4, "a":4,
773 "odt":4, "ras":4, "cas":4, "we":4,
776 # Get SPI resource pins
778 if platform
is not None and \
779 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
780 # Override here to get FlashResource out of the way and enable Tercel
781 # direct access to the SPI flash.
782 # each pin needs a separate direction control
785 Subsignal("dq0", Pins("W2", dir="io")),
786 Subsignal("dq1", Pins("V2", dir="io")),
787 Subsignal("dq2", Pins("Y2", dir="io")),
788 Subsignal("dq3", Pins("W1", dir="io")),
789 Subsignal("cs_n", Pins("R2", dir="o")),
790 Attrs(PULLMODE
="NONE", DRIVE
="4", IO_TYPE
="LVCMOS33"))
792 platform
.add_resources(spi_0_ios
)
793 spi_0_pins
= platform
.request("spi_0", 0, dir={"cs_n":"o"},
794 xdr
={"dq0":1, "dq1": 1,
798 if platform
is not None and \
800 # each pin needs a separate direction control
803 Subsignal("dq0", Pins("K17", dir="io")),
804 Subsignal("dq1", Pins("K18", dir="io")),
805 Subsignal("dq2", Pins("L14", dir="io")),
806 Subsignal("dq3", Pins("M14", dir="io")),
807 Subsignal("cs_n", Pins("L13", dir="o")),
808 Subsignal("clk", Pins("L16", dir="o")),
809 Attrs(PULLMODE
="NONE", DRIVE
="4", IO_TYPE
="LVCMOS33"))
811 platform
.add_resources(spi_0_ios
)
812 spi_0_pins
= platform
.request("spi_0", 0)
814 print ("spiflash pins", spi_0_pins
)
816 # Get Ethernet RMII resource pins
818 if False and platform
is not None and \
819 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
820 # Mainly on X3 connector, MDIO on X4 due to lack of pins
822 Resource("ethmac_0", 0,
823 Subsignal("mtx_clk", Pins("B19", dir="i")),
824 Subsignal("mtxd", Pins("B12 B9 E6 D6", dir="o")),
825 Subsignal("mtxen", Pins("E7", dir="o")),
826 Subsignal("mtxerr", Pins("D7", dir="o")),
827 Subsignal("mrx_clk", Pins("B11", dir="i")),
828 Subsignal("mrxd", Pins("B6 E9 D9 B8", dir="i")),
829 Subsignal("mrxdv", Pins("C8", dir="i")),
830 Subsignal("mrxerr", Pins("D8", dir="i")),
831 Subsignal("mcoll", Pins("E8", dir="i")),
832 Subsignal("mcrs", Pins("C7", dir="i")),
833 Subsignal("mdc", Pins("B18", dir="o")),
834 Subsignal("md", Pins("A18", dir="io")),
835 Attrs(PULLMODE
="NONE", DRIVE
="8", SLEWRATE
="FAST",
838 platform
.add_resources(ethmac_0_ios
)
839 ethmac_0_pins
= platform
.request("ethmac_0", 0,
840 dir={"mtx_clk":"i", "mtxd":"o",
842 "mtxerr":"o", "mrx_clk":"i",
844 "mrxdv":"i", "mrxerr":"i",
846 "mcrs":"i", "mdc":"o", "md":"io"},
847 xdr
={"mtx_clk": 0, "mtxd": 0,
849 "mtxerr": 0, "mrx_clk": 0,
851 "mrxdv": 0, "mrxerr": 0,
853 "mcrs": 0, "mdc": 0, "md": 0})
854 print ("ethmac pins", ethmac_0_pins
)
859 hyperram_pins
= HyperRAMPads()
860 elif fpga
in ['isim']:
861 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
862 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
863 rwds
="C14", rst_n
="E13", ck_p
="D13",
864 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
865 platform
.add_resources(hyperram_ios
)
866 hyperram_pins
= platform
.request("hyperram")
867 print ("isim a7 hyperram", hyperram_ios
)
868 # Digilent Arty A7-100t
869 elif platform
is not None and fpga
in ['arty_a7']:
870 hyperram_ios
= HyperRAMResource(0, cs_n
="V12 V14 U12 U14",
871 dq
="D4 D3 F4 F3 G2 H2 D2 E2",
872 rwds
="U13", rst_n
="T13", ck_p
="V10",
873 # ck_n="V11" - for later (DDR)
874 attrs
=Attrs(IOSTANDARD
="LVCMOS33"))
875 platform
.add_resources(hyperram_ios
)
876 hyperram_pins
= platform
.request("hyperram")
877 print ("arty a7 hyperram", hyperram_ios
)
879 elif False and platform
is not None and fpga
in \
880 ['versa_ecp5', 'versa_ecp5_85']:
881 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
882 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
883 rwds
="C14", rst_n
="E13", ck_p
="D13",
884 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
885 platform
.add_resources(hyperram_ios
)
886 hyperram_pins
= platform
.request("hyperram")
887 print ("versa ecp5 hyperram", hyperram_ios
)
888 print ("hyperram pins", hyperram_pins
)
891 soc
= DDR3SoC(fpga
=fpga
, dram_cls
=dram_cls
,
892 # check microwatt_soc.h for these
893 ddrphy_addr
=0xfff00000, # DRAM_INIT_BASE, PHY address
894 dramcore_addr
=0xc8000000, # DRAM_CTRL_BASE
895 ddr_addr
=0x00000000, # DRAM_BASE
896 spi0_addr
=0xf0000000, # SPI0_BASE
897 spi0_cfg_addr
=0xc0006000, # SPI0_CTRL_BASE
898 eth0_cfg_addr
=0xc000c000, # ETH0_CTRL_BASE (4k)
899 eth0_irqno
=1, # ETH0_IRQ number (match microwatt)
900 hyperram_addr
=0xa0000000, # HYPERRAM_BASE
905 uart_irqno
=0, # UART_IRQ number (match microwatt)
906 uart_addr
=0xc0002000, # UART0_ADDR
907 spi_0_pins
=spi_0_pins
,
908 ethmac_0_pins
=ethmac_0_pins
,
909 hyperram_pins
=hyperram_pins
,
911 xics_icp_addr
=0xc000_4000, # XICS_ICP_BASE
912 xics_ics_addr
=0xc000_5000, # XICS_ICS_BASE
916 if toolchain
== 'Trellis':
917 # add -abc9 option to yosys synth_ecp5
918 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
919 #os.environ['NMIGEN_synth_opts'] = '-abc9'
920 os
.environ
['NMIGEN_synth_opts'] = '-nowidelut'
922 if platform
is not None:
923 # build and upload it
925 platform
.build(soc
, do_program
=False,
926 do_build
=True, build_dir
="build_simsoc")
928 platform
.build(soc
, do_program
=True)
930 # for now, generate verilog
931 vl
= verilog
.convert(soc
, ports
=soc
.ports())
932 with
open("ls2.v", "w") as f
:
936 # urrr this gets exec()d by the build process without arguments
937 # which screws up. use the arty_a7_ls2.py etc. with no arguments
938 if __name__
== '__main__':
941 if len(sys
.argv
) >= 2:
943 if len(sys
.argv
) >= 3:
944 firmware
= sys
.argv
[2]
945 build_platform(fpga
, firmware
)