1 # This file is Copyright (c) 2014-2020 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
3 # This file is Copyright (c) 2019 Gabriel L. Somlo <somlo@cmu.edu>
9 from math
import log2
, ceil
13 from litex
.soc
.cores
import cpu
14 from litex
.soc
.cores
.identifier
import Identifier
15 from litex
.soc
.cores
.timer
import Timer
16 from litex
.soc
.cores
.spi_flash
import SpiFlash
17 from litex
.soc
.cores
.spi
import SPIMaster
19 from litex
.soc
.interconnect
.csr
import *
20 from litex
.soc
.interconnect
import csr_bus
21 from litex
.soc
.interconnect
import wishbone
22 from litex
.soc
.interconnect
import wishbone2csr
23 from litex
.soc
.interconnect
import axi
25 logging
.basicConfig(level
=logging
.INFO
)
27 # Helpers ------------------------------------------------------------------------------------------
32 def colorer(s
, color
="bright"):
39 "underline": "\x1b[4m"}[color
]
41 return header
+ str(s
) + trailer
43 def build_time(with_time
=True):
44 fmt
= "%Y-%m-%d %H:%M:%S" if with_time
else "%Y-%m-%d"
45 return datetime
.datetime
.fromtimestamp(time
.time()).strftime(fmt
)
47 # SoCConstant --------------------------------------------------------------------------------------
49 def SoCConstant(value
):
52 # SoCRegion ----------------------------------------------------------------------------------------
55 def __init__(self
, origin
=None, size
=None, mode
="rw", cached
=True, linker
=False):
56 self
.logger
= logging
.getLogger("SoCRegion")
59 if size
!= 2**log2_int(size
, False):
60 self
.logger
.info("Region size {} internally from {} to {}.".format(
61 colorer("rounded", color
="cyan"),
62 colorer("0x{:08x}".format(size
)),
63 colorer("0x{:08x}".format(2**log2_int(size
, False)))))
64 self
.size_pow2
= 2**log2_int(size
, False)
69 def decoder(self
, bus
):
72 if (origin
& (size
- 1)) != 0:
73 self
.logger
.error("Origin needs to be aligned on size:")
74 self
.logger
.error(self
)
76 origin
>>= int(log2(bus
.data_width
//8)) # bytes to words aligned
77 size
>>= int(log2(bus
.data_width
//8)) # bytes to words aligned
78 return lambda a
: (a
[log2_int(size
):] == (origin
>> log2_int(size
)))
82 if self
.origin
is not None:
83 r
+= "Origin: {}, ".format(colorer("0x{:08x}".format(self
.origin
)))
84 if self
.size
is not None:
85 r
+= "Size: {}, ".format(colorer("0x{:08x}".format(self
.size
)))
86 r
+= "Mode: {}, ".format(colorer(self
.mode
.upper()))
87 r
+= "Cached: {} ".format(colorer(self
.cached
))
88 r
+= "Linker: {}".format(colorer(self
.linker
))
91 class SoCIORegion(SoCRegion
): pass
93 # SoCCSRRegion -------------------------------------------------------------------------------------
96 def __init__(self
, origin
, busword
, obj
):
98 self
.busword
= busword
101 # SoCBusHandler ------------------------------------------------------------------------------------
103 class SoCBusHandler(Module
):
104 supported_standard
= ["wishbone"]
105 supported_data_width
= [32, 64]
106 supported_address_width
= [32]
108 # Creation -------------------------------------------------------------------------------------
109 def __init__(self
, standard
, data_width
=32, address_width
=32, timeout
=1e6
, reserved_regions
={}):
110 self
.logger
= logging
.getLogger("SoCBusHandler")
111 self
.logger
.info("Creating Bus Handler...")
114 if standard
not in self
.supported_standard
:
115 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
116 colorer("Bus standard", color
="red"),
118 colorer(", ".join(self
.supported_standard
))))
122 if data_width
not in self
.supported_data_width
:
123 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
124 colorer("Data Width", color
="red"),
126 colorer(", ".join(str(x
) for x
in self
.supported_data_width
))))
129 # Check Address Width
130 if address_width
not in self
.supported_address_width
:
131 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
132 colorer("Address Width", color
="red"),
134 colorer(", ".join(str(x
) for x
in self
.supported_address_width
))))
138 self
.standard
= standard
139 self
.data_width
= data_width
140 self
.address_width
= address_width
145 self
.timeout
= timeout
146 self
.logger
.info("{}-bit {} Bus, {}GiB Address Space.".format(
147 colorer(data_width
), colorer(standard
), colorer(2**address_width
/2**30)))
149 # Adding reserved regions
150 self
.logger
.info("Adding {} Bus Regions...".format(colorer("reserved", color
="cyan")))
151 for name
, region
in reserved_regions
.items():
152 if isinstance(region
, int):
153 region
= SoCRegion(origin
=region
, size
=0x1000000)
154 self
.add_region(name
, region
)
156 self
.logger
.info("Bus Handler {}.".format(colorer("created", color
="green")))
158 # Add/Allog/Check Regions ----------------------------------------------------------------------
159 def add_region(self
, name
, region
):
161 if name
in self
.regions
.keys() or name
in self
.io_regions
.keys():
162 self
.logger
.error("{} already declared as Region:".format(colorer(name
, color
="red")))
163 self
.logger
.error(self
)
165 # Check if SoCIORegion
166 if isinstance(region
, SoCIORegion
):
167 self
.io_regions
[name
] = region
168 overlap
= self
.check_regions_overlap(self
.io_regions
)
169 if overlap
is not None:
170 self
.logger
.error("IO Region {} between {} and {}:".format(
171 colorer("overlap", color
="red"),
173 colorer(overlap
[1])))
174 self
.logger
.error(str(self
.io_regions
[overlap
[0]]))
175 self
.logger
.error(str(self
.io_regions
[overlap
[1]]))
177 self
.logger
.info("{} Region {} at {}.".format(
178 colorer(name
, color
="underline"),
179 colorer("added", color
="green"),
182 elif isinstance(region
, SoCRegion
):
183 # If no origin specified, allocate region.
184 if region
.origin
is None:
186 region
= self
.alloc_region(name
, region
.size
, region
.cached
)
187 self
.regions
[name
] = region
188 # Else add region and check for overlaps.
190 if not region
.cached
:
191 if not self
.check_region_is_io(region
):
192 self
.logger
.error("{} Region {}: {}.".format(
194 colorer("not in IO region", color
="red"),
196 self
.logger
.error(self
)
198 self
.regions
[name
] = region
199 overlap
= self
.check_regions_overlap(self
.regions
)
200 if overlap
is not None:
201 self
.logger
.error("Region {} between {} and {}:".format(
202 colorer("overlap", color
="red"),
204 colorer(overlap
[1])))
205 self
.logger
.error(str(self
.regions
[overlap
[0]]))
206 self
.logger
.error(str(self
.regions
[overlap
[1]]))
208 self
.logger
.info("{} Region {} at {}.".format(
209 colorer(name
, color
="underline"),
210 colorer("allocated" if allocated
else "added", color
="cyan" if allocated
else "green"),
213 self
.logger
.error("{} is not a supported Region.".format(colorer(name
, color
="red")))
216 def alloc_region(self
, name
, size
, cached
=True):
217 self
.logger
.info("Allocating {} Region of size {}...".format(
218 colorer("Cached" if cached
else "IO"),
219 colorer("0x{:08x}".format(size
))))
221 # Limit Search Regions
223 search_regions
= self
.io_regions
225 search_regions
= {"main": SoCRegion(origin
=0x00000000, size
=2**self
.address_width
-1)}
227 # Iterate on Search_Regions to find a Candidate
228 for _
, search_region
in search_regions
.items():
229 origin
= search_region
.origin
230 while (origin
+ size
) < (search_region
.origin
+ search_region
.size_pow2
):
231 # Create a Candicate.
232 candidate
= SoCRegion(origin
=origin
, size
=size
, cached
=cached
)
234 # Check Candidate does not overlap with allocated existing regions
235 for _
, allocated
in self
.regions
.items():
236 if self
.check_regions_overlap({"0": allocated
, "1": candidate
}) is not None:
237 origin
= allocated
.origin
+ allocated
.size_pow2
241 # If no overlap, the Candidate is selected
244 self
.logger
.error("Not enough Address Space to allocate Region.")
247 def check_regions_overlap(self
, regions
, check_linker
=False):
249 while i
< len(regions
):
250 n0
= list(regions
.keys())[i
]
252 for n1
in list(regions
.keys())[i
+1:]:
254 if r0
.linker
or r1
.linker
:
257 if r0
.origin
>= (r1
.origin
+ r1
.size_pow2
):
259 if r1
.origin
>= (r0
.origin
+ r0
.size_pow2
):
265 def check_region_is_in(self
, region
, container
):
267 if not (region
.origin
>= container
.origin
):
269 if not ((region
.origin
+ region
.size
) < (container
.origin
+ container
.size
)):
273 def check_region_is_io(self
, region
):
275 for _
, io_region
in self
.io_regions
.items():
276 if self
.check_region_is_in(region
, io_region
):
280 # Add Master/Slave -----------------------------------------------------------------------------
281 def add_adapter(self
, name
, interface
, direction
="m2s"):
282 assert direction
in ["m2s", "s2m"]
283 if interface
.data_width
!= self
.data_width
:
284 self
.logger
.info("{} Bus {} from {}-bit to {}-bit.".format(
286 colorer("converted", color
="cyan"),
287 colorer(interface
.data_width
),
288 colorer(self
.data_width
)))
289 new_interface
= wishbone
.Interface(data_width
=self
.data_width
)
290 if direction
== "m2s":
291 converter
= wishbone
.Converter(master
=interface
, slave
=new_interface
)
292 if direction
== "s2m":
293 converter
= wishbone
.Converter(master
=new_interface
, slave
=interface
)
294 self
.submodules
+= converter
299 def add_master(self
, name
=None, master
=None):
301 name
= "master{:d}".format(len(self
.masters
))
302 if name
in self
.masters
.keys():
303 self
.logger
.error("{} {} as Bus Master:".format(
305 colorer("already declared", color
="red")))
306 self
.logger
.error(self
)
308 master
= self
.add_adapter(name
, master
, "m2s")
309 self
.masters
[name
] = master
310 self
.logger
.info("{} {} as Bus Master.".format(
311 colorer(name
, color
="underline"),
312 colorer("added", color
="green")))
314 def add_slave(self
, name
=None, slave
=None, region
=None):
315 no_name
= name
is None
316 no_region
= region
is None
317 if no_name
and no_region
:
318 self
.logger
.error("Please {} {} or/and {} of Bus Slave.".format(
319 colorer("specify", color
="red"),
324 name
= "slave{:d}".format(len(self
.slaves
))
326 region
= self
.regions
.get(name
, None)
328 self
.logger
.error("{} Region {}.".format(
330 colorer("not found", color
="red")))
333 self
.add_region(name
, region
)
334 if name
in self
.slaves
.keys():
335 self
.logger
.error("{} {} as Bus Slave:".format(
337 colorer("already declared", color
="red")))
338 self
.logger
.error(self
)
340 slave
= self
.add_adapter(name
, slave
, "s2m")
341 self
.slaves
[name
] = slave
342 self
.logger
.info("{} {} as Bus Slave.".format(
343 colorer(name
, color
="underline"),
344 colorer("added", color
="green")))
346 # Str ------------------------------------------------------------------------------------------
348 r
= "{}-bit {} Bus, {}GiB Address Space.\n".format(
349 colorer(self
.data_width
), colorer(self
.standard
), colorer(2**self
.address_width
/2**30))
350 r
+= "IO Regions: ({})\n".format(len(self
.io_regions
.keys())) if len(self
.io_regions
.keys()) else ""
351 io_regions
= {k
: v
for k
, v
in sorted(self
.io_regions
.items(), key
=lambda item
: item
[1].origin
)}
352 for name
, region
in io_regions
.items():
353 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
354 r
+= "Bus Regions: ({})\n".format(len(self
.regions
.keys())) if len(self
.regions
.keys()) else ""
355 regions
= {k
: v
for k
, v
in sorted(self
.regions
.items(), key
=lambda item
: item
[1].origin
)}
356 for name
, region
in regions
.items():
357 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
358 r
+= "Bus Masters: ({})\n".format(len(self
.masters
.keys())) if len(self
.masters
.keys()) else ""
359 for name
in self
.masters
.keys():
360 r
+= "- {}\n".format(colorer(name
, color
="underline"))
361 r
+= "Bus Slaves: ({})\n".format(len(self
.slaves
.keys())) if len(self
.slaves
.keys()) else ""
362 for name
in self
.slaves
.keys():
363 r
+= "- {}\n".format(colorer(name
, color
="underline"))
367 # SoCLocHandler --------------------------------------------------------------------------------------
369 class SoCLocHandler(Module
):
370 # Creation -------------------------------------------------------------------------------------
371 def __init__(self
, name
, n_locs
):
376 # Add ------------------------------------------------------------------------------------------
377 def add(self
, name
, n
=None, use_loc_if_exists
=False):
379 if not (use_loc_if_exists
and name
in self
.locs
.keys()):
380 if name
in self
.locs
.keys():
381 self
.logger
.error("{} {} name {}.".format(
382 colorer(name
), self
.name
, colorer("already used", color
="red")))
383 self
.logger
.error(self
)
385 if n
in self
.locs
.values():
386 self
.logger
.error("{} {} Location {}.".format(
387 colorer(n
), self
.name
, colorer("already used", color
="red")))
388 self
.logger
.error(self
)
395 self
.logger
.error("{} {} Location should be {}.".format(
398 colorer("positive", color
="red")))
401 self
.logger
.error("{} {} Location {} than maximum: {}.".format(
404 colorer("higher", color
="red"),
405 colorer(self
.n_locs
)))
410 self
.logger
.info("{} {} {} at Location {}.".format(
411 colorer(name
, color
="underline"),
413 colorer("allocated" if allocated
else "added", color
="cyan" if allocated
else "green"),
416 # Alloc ----------------------------------------------------------------------------------------
417 def alloc(self
, name
):
418 for n
in range(self
.n_locs
):
419 if n
not in self
.locs
.values():
421 self
.logger
.error("Not enough Locations.")
422 self
.logger
.error(self
)
425 # Str ------------------------------------------------------------------------------------------
427 r
= "{} Locations: ({})\n".format(self
.name
, len(self
.locs
.keys())) if len(self
.locs
.keys()) else ""
428 locs
= {k
: v
for k
, v
in sorted(self
.locs
.items(), key
=lambda item
: item
[1])}
430 for name
in locs
.keys():
431 if len(name
) > length
: length
= len(name
)
432 for name
in locs
.keys():
433 r
+= "- {}{}: {}\n".format(colorer(name
, color
="underline"), " "*(length
+ 1 - len(name
)), colorer(self
.locs
[name
]))
436 # SoCCSRHandler ------------------------------------------------------------------------------------
438 class SoCCSRHandler(SoCLocHandler
):
439 supported_data_width
= [8, 32]
440 supported_address_width
= [14+i
for i
in range(4)]
441 supported_alignment
= [32, 64]
442 supported_paging
= [0x800*2**i
for i
in range(4)]
444 # Creation -------------------------------------------------------------------------------------
445 def __init__(self
, data_width
=32, address_width
=14, alignment
=32, paging
=0x800, reserved_csrs
={}):
446 SoCLocHandler
.__init
__(self
, "CSR", n_locs
=alignment
//8*(2**address_width
)//paging
)
447 self
.logger
= logging
.getLogger("SoCCSRHandler")
448 self
.logger
.info("Creating CSR Handler...")
451 if data_width
not in self
.supported_data_width
:
452 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
453 colorer("Data Width", color
="red"),
455 colorer(", ".join(str(x
) for x
in self
.supported_data_width
))))
458 # Check Address Width
459 if address_width
not in self
.supported_address_width
:
460 self
.logger
.error("Unsupported {} {} supporteds: {:s}".format(
461 colorer("Address Width", color
="red"),
462 colorer(address_width
),
463 colorer(", ".join(str(x
) for x
in self
.supported_address_width
))))
467 if alignment
not in self
.supported_alignment
:
468 self
.logger
.error("Unsupported {}: {} supporteds: {:s}".format(
469 colorer("Alignment", color
="red"),
471 colorer(", ".join(str(x
) for x
in self
.supported_alignment
))))
473 if data_width
> alignment
:
474 self
.logger
.error("Alignment ({}) {} Data Width ({})".format(
476 colorer("should be >=", color
="red"),
477 colorer(data_width
)))
481 if paging
not in self
.supported_paging
:
482 self
.logger
.error("Unsupported {} 0x{}, supporteds: {:s}".format(
483 colorer("Paging", color
="red"),
484 colorer("{:x}".format(paging
)),
485 colorer(", ".join("0x{:x}".format(x
) for x
in self
.supported_paging
))))
489 self
.data_width
= data_width
490 self
.address_width
= address_width
491 self
.alignment
= alignment
495 self
.logger
.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).".format(
496 colorer(self
.data_width
),
497 colorer(self
.alignment
),
498 colorer(2**self
.address_width
/2**10),
499 colorer(self
.paging
),
500 colorer(self
.n_locs
)))
502 # Adding reserved CSRs
503 self
.logger
.info("Adding {} CSRs...".format(colorer("reserved", color
="cyan")))
504 for name
, n
in reserved_csrs
.items():
507 self
.logger
.info("CSR Handler {}.".format(colorer("created", color
="green")))
509 # Update CSR Alignment ----------------------------------------------------------------------------
510 def update_alignment(self
, alignment
):
512 if alignment
not in self
.supported_alignment
:
513 self
.logger
.error("Unsupported {}: {} supporteds: {:s}".format(
514 colorer("Alignment", color
="red"),
516 colorer(", ".join(str(x
) for x
in self
.supported_alignment
))))
518 self
.logger
.info("Alignment {} from {}-bit to {}-bit.".format(
519 colorer("updated", color
="cyan"),
520 colorer(self
.alignment
),
522 self
.alignment
= alignment
524 # Add Master -----------------------------------------------------------------------------------
525 def add_master(self
, name
=None, master
=None):
527 name
= "master{:d}".format(len(self
.masters
))
528 if name
in self
.masters
.keys():
529 self
.logger
.error("{} {} as CSR Master:".format(
531 colorer("already declared", color
="red")))
532 self
.logger
.error(self
)
534 if master
.data_width
!= self
.data_width
:
535 self
.logger
.error("{} Master/Handler Data Width {} ({} vs {}).".format(
537 colorer("missmatch", color
="red"),
538 colorer(master
.data_width
),
539 colorer(self
.data_width
)))
541 self
.masters
[name
] = master
542 self
.logger
.info("{} {} as CSR Master.".format(
543 colorer(name
, color
="underline"),
544 colorer("added", color
="green")))
546 # Add Region -----------------------------------------------------------------------------------
547 def add_region(self
, name
, region
):
549 self
.regions
[name
] = region
551 # Address map ----------------------------------------------------------------------------------
552 def address_map(self
, name
, memory
):
553 if memory
is not None:
554 name
= name
+ "_" + memory
.name_override
555 if self
.locs
.get(name
, None) is None:
556 self
.logger
.error("CSR {} {}.".format(
558 colorer("not found", color
="red")))
559 self
.logger
.error(self
)
561 return self
.locs
[name
]
563 # Str ------------------------------------------------------------------------------------------
565 r
= "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
566 colorer(self
.data_width
),
567 colorer(self
.alignment
),
568 colorer(2**self
.address_width
/2**10),
569 colorer(self
.paging
),
570 colorer(self
.n_locs
))
571 r
+= SoCLocHandler
.__str
__(self
)
575 # SoCIRQHandler ------------------------------------------------------------------------------------
577 class SoCIRQHandler(SoCLocHandler
):
578 # Creation -------------------------------------------------------------------------------------
579 def __init__(self
, n_irqs
=32, reserved_irqs
={}):
580 SoCLocHandler
.__init
__(self
, "IRQ", n_locs
=n_irqs
)
581 self
.logger
= logging
.getLogger("SoCIRQHandler")
582 self
.logger
.info("Creating IRQ Handler...")
586 self
.logger
.error("Unsupported IRQs number: {} supporteds: {:s}".format(
587 colorer(n
, color
="red"), colorer("Up to 32", color
="green")))
591 self
.logger
.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs
)))
593 # Adding reserved IRQs
594 self
.logger
.info("Adding {} IRQs...".format(colorer("reserved", color
="cyan")))
595 for name
, n
in reserved_irqs
.items():
598 self
.logger
.info("IRQ Handler {}.".format(colorer("created", color
="green")))
600 # Str ------------------------------------------------------------------------------------------
602 r
="IRQ Handler (up to {} Locations).\n".format(colorer(self
.n_locs
))
603 r
+= SoCLocHandler
.__str
__(self
)
607 # SoCController ------------------------------------------------------------------------------------
609 class SoCController(Module
, AutoCSR
):
616 self
._reset
= CSRStorage(1, description
="""Write a ``1`` to this register to reset the SoC.""")
618 self
._scratch
= CSRStorage(32, reset
=0x12345678, description
="""
619 Use this register as a scratch space to verify that software read/write accesses
620 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
621 can be used to verify endianness.""")
623 self
._bus
_errors
= CSRStatus(32, description
="Total number of Wishbone bus errors (timeouts) since start.")
629 self
.reset
= Signal()
630 self
.comb
+= self
.reset
.eq(self
._reset
.re
)
634 self
.bus_error
= Signal()
635 bus_errors
= Signal(32)
637 If(bus_errors
!= (2**len(bus_errors
)-1),
638 If(self
.bus_error
, bus_errors
.eq(bus_errors
+ 1))
641 self
.comb
+= self
._bus
_errors
.status
.eq(bus_errors
)
643 # SoC ----------------------------------------------------------------------------------------------
647 def __init__(self
, platform
, sys_clk_freq
,
649 bus_standard
= "wishbone",
651 bus_address_width
= 32,
653 bus_reserved_regions
= {},
656 csr_address_width
= 14,
659 csr_reserved_csrs
= {},
662 irq_reserved_irqs
= {},
665 self
.logger
= logging
.getLogger("SoC")
666 self
.logger
.info(colorer(" __ _ __ _ __ ", color
="bright"))
667 self
.logger
.info(colorer(" / / (_) /____ | |/_/ ", color
="bright"))
668 self
.logger
.info(colorer(" / /__/ / __/ -_)> < ", color
="bright"))
669 self
.logger
.info(colorer(" /____/_/\\__/\\__/_/|_| ", color
="bright"))
670 self
.logger
.info(colorer(" Build your hardware, easily!", color
="bright"))
672 self
.logger
.info(colorer("-"*80, color
="bright"))
673 self
.logger
.info(colorer("Creating SoC... ({})".format(build_time())))
674 self
.logger
.info(colorer("-"*80, color
="bright"))
675 self
.logger
.info("FPGA device : {}.".format(platform
.device
))
676 self
.logger
.info("System clock: {:3.2f}MHz.".format(sys_clk_freq
/1e6
))
678 # SoC attributes ---------------------------------------------------------------------------
679 self
.platform
= platform
680 self
.sys_clk_freq
= sys_clk_freq
682 self
.csr_regions
= {}
684 # SoC Bus Handler --------------------------------------------------------------------------
685 self
.submodules
.bus
= SoCBusHandler(
686 standard
= bus_standard
,
687 data_width
= bus_data_width
,
688 address_width
= bus_address_width
,
689 timeout
= bus_timeout
,
690 reserved_regions
= bus_reserved_regions
,
693 # SoC Bus Handler --------------------------------------------------------------------------
694 self
.submodules
.csr
= SoCCSRHandler(
695 data_width
= csr_data_width
,
696 address_width
= csr_address_width
,
697 alignment
= csr_alignment
,
699 reserved_csrs
= csr_reserved_csrs
,
702 # SoC IRQ Handler --------------------------------------------------------------------------
703 self
.submodules
.irq
= SoCIRQHandler(
705 reserved_irqs
= irq_reserved_irqs
708 self
.logger
.info(colorer("-"*80, color
="bright"))
709 self
.logger
.info(colorer("Initial SoC:"))
710 self
.logger
.info(colorer("-"*80, color
="bright"))
711 self
.logger
.info(self
.bus
)
712 self
.logger
.info(self
.csr
)
713 self
.logger
.info(self
.irq
)
714 self
.logger
.info(colorer("-"*80, color
="bright"))
716 self
.add_config("CLOCK_FREQUENCY", int(sys_clk_freq
))
718 # SoC Helpers ----------------------------------------------------------------------------------
719 def check_if_exists(self
, name
):
720 if hasattr(self
, name
):
721 self
.logger
.error("{} SubModule already {}.".format(
723 colorer("declared", color
="red")))
726 def add_constant(self
, name
, value
=None):
728 if name
in self
.constants
.keys():
729 self
.logger
.error("{} Constant already {}.".format(
731 colorer("declared", color
="red")))
733 self
.constants
[name
] = SoCConstant(value
)
735 def add_config(self
, name
, value
=None):
736 name
= "CONFIG_" + name
737 if isinstance(value
, str):
738 self
.add_constant(name
+ "_" + value
)
740 self
.add_constant(name
, value
)
742 # SoC Main Components --------------------------------------------------------------------------
743 def add_controller(self
, name
="ctrl", **kwargs
):
744 self
.check_if_exists(name
)
745 setattr(self
.submodules
, name
, SoCController(**kwargs
))
746 self
.csr
.add(name
, use_loc_if_exists
=True)
748 def add_ram(self
, name
, origin
, size
, contents
=[], mode
="rw"):
749 ram_bus
= wishbone
.Interface(data_width
=self
.bus
.data_width
)
750 ram
= wishbone
.SRAM(size
, bus
=ram_bus
, init
=contents
, read_only
=(mode
== "r"))
751 self
.bus
.add_slave(name
, ram
.bus
, SoCRegion(origin
=origin
, size
=size
, mode
=mode
))
752 self
.check_if_exists(name
)
753 self
.logger
.info("RAM {} {} {}.".format(
755 colorer("added", color
="green"),
756 self
.bus
.regions
[name
]))
757 setattr(self
.submodules
, name
, ram
)
759 def add_rom(self
, name
, origin
, size
, contents
=[]):
760 self
.add_ram(name
, origin
, size
, contents
, mode
="r")
762 def add_csr_bridge(self
, origin
):
763 self
.submodules
.csr_bridge
= wishbone2csr
.WB2CSR(
764 bus_csr
= csr_bus
.Interface(
765 address_width
= self
.csr
.address_width
,
766 data_width
= self
.csr
.data_width
))
767 csr_size
= 2**(self
.csr
.address_width
+ 2)
768 csr_region
= SoCRegion(origin
=origin
, size
=csr_size
, cached
=False)
769 self
.bus
.add_slave("csr", self
.csr_bridge
.wishbone
, csr_region
)
770 self
.csr
.add_master(name
="bridge", master
=self
.csr_bridge
.csr
)
771 self
.add_config("CSR_DATA_WIDTH", self
.csr
.data_width
)
772 self
.add_config("CSR_ALIGNMENT", self
.csr
.alignment
)
774 def add_cpu(self
, name
="vexriscv", variant
="standard", cls
=None, reset_address
=None):
775 if name
not in cpu
.CPUS
.keys():
776 self
.logger
.error("{} CPU {}, supporteds: {}".format(
778 colorer("not supported", color
="red"),
779 colorer(", ".join(cpu
.CPUS
.keys()))))
782 cpu_cls
= cls
if cls
is not None else cpu
.CPUS
[name
]
783 self
.submodules
.cpu
= cpu_cls(self
.platform
, variant
)
784 # Update SoC with CPU constraints
785 for n
, (origin
, size
) in enumerate(self
.cpu
.io_regions
.items()):
786 self
.bus
.add_region("io{}".format(n
), SoCIORegion(origin
=origin
, size
=size
, cached
=False))
787 self
.mem_map
.update(self
.cpu
.mem_map
) # FIXME
788 # Add Bus Masters/CSR/IRQs
789 if not isinstance(self
.cpu
, cpu
.CPUNone
):
790 self
.csr
.update_alignment(self
.cpu
.data_width
)
791 if reset_address
is None:
792 reset_address
= self
.mem_map
["rom"]
793 self
.cpu
.set_reset_address(reset_address
)
794 for n
, cpu_bus
in enumerate(self
.cpu
.periph_buses
):
795 self
.bus
.add_master(name
="cpu_bus{}".format(n
), master
=cpu_bus
)
796 self
.csr
.add("cpu", use_loc_if_exists
=True)
797 if hasattr(self
.cpu
, "interrupt"):
798 for name
, loc
in self
.cpu
.interrupts
.items():
799 self
.irq
.add(name
, loc
)
800 self
.add_config("CPU_HAS_INTERRUPT")
803 if hasattr(self
, "ctrl"):
804 if hasattr(self
.ctrl
, "reset"):
805 self
.comb
+= self
.cpu
.reset
.eq(self
.ctrl
.reset
)
806 self
.add_config("CPU_RESET_ADDR", reset_address
)
808 self
.add_config("CPU_TYPE", str(name
))
809 self
.add_config("CPU_VARIANT", str(variant
.split('+')[0]))
810 self
.add_constant("CONFIG_CPU_HUMAN_NAME", getattr(self
.cpu
, "human_name", "Unknown"))
811 if hasattr(self
.cpu
, "nop"):
812 self
.add_constant("CONFIG_CPU_NOP", self
.cpu
.nop
)
814 def add_timer(self
, name
="timer0"):
815 self
.check_if_exists(name
)
816 setattr(self
.submodules
, name
, Timer())
817 self
.csr
.add(name
, use_loc_if_exists
=True)
818 if hasattr(self
.cpu
, "interrupt"):
819 self
.irq
.add(name
, use_loc_if_exists
=True)
821 # SoC finalization -----------------------------------------------------------------------------
822 def do_finalize(self
):
823 self
.logger
.info(colorer("-"*80, color
="bright"))
824 self
.logger
.info(colorer("Finalized SoC:"))
825 self
.logger
.info(colorer("-"*80, color
="bright"))
826 self
.logger
.info(self
.bus
)
827 self
.logger
.info(self
.csr
)
828 self
.logger
.info(self
.irq
)
829 self
.logger
.info(colorer("-"*80, color
="bright"))
831 # SoC Bus Interconnect ---------------------------------------------------------------------
832 if len(self
.bus
.masters
) and len(self
.bus
.slaves
):
833 # If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
834 if ((len(self
.bus
.masters
) == 1) and
835 (len(self
.bus
.slaves
) == 1) and
836 (next(iter(self
.bus
.regions
.values())).origin
== 0)):
837 self
.submodules
.bus_interconnect
= wishbone
.InterconnectPointToPoint(
838 master
= next(iter(self
.bus
.masters
.values())),
839 slave
= next(iter(self
.bus
.slaves
.values())))
840 # Otherwise, use InterconnectShared.
842 self
.submodules
.bus_interconnect
= wishbone
.InterconnectShared(
843 masters
= self
.bus
.masters
.values(),
844 slaves
= [(self
.bus
.regions
[n
].decoder(self
.bus
), s
) for n
, s
in self
.bus
.slaves
.items()],
846 timeout_cycles
= self
.bus
.timeout
)
847 if hasattr(self
, "ctrl") and self
.bus
.timeout
is not None:
848 if hasattr(self
.ctrl
, "bus_error"):
849 self
.comb
+= self
.ctrl
.bus_error
.eq(self
.bus_interconnect
.timeout
.error
)
850 self
.bus
.logger
.info("Interconnect: {} ({} <-> {}).".format(
851 colorer(self
.bus_interconnect
.__class
__.__name
__),
852 colorer(len(self
.bus
.masters
)),
853 colorer(len(self
.bus
.slaves
))))
855 # SoC CSR Interconnect ---------------------------------------------------------------------
856 self
.submodules
.csr_bankarray
= csr_bus
.CSRBankArray(self
,
857 address_map
= self
.csr
.address_map
,
858 data_width
= self
.csr
.data_width
,
859 address_width
= self
.csr
.address_width
,
860 alignment
= self
.csr
.alignment
,
861 paging
= self
.csr
.paging
,
862 soc_bus_data_width
= self
.bus
.data_width
)
863 if len(self
.csr
.masters
):
864 self
.submodules
.csr_interconnect
= csr_bus
.InterconnectShared(
865 masters
= list(self
.csr
.masters
.values()),
866 slaves
= self
.csr_bankarray
.get_buses())
869 for name
, csrs
, mapaddr
, rmap
in self
.csr_bankarray
.banks
:
870 self
.csr
.add_region(name
, SoCCSRRegion(
871 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
872 busword
= self
.csr
.data_width
,
876 for name
, memory
, mapaddr
, mmap
in self
.csr_bankarray
.srams
:
877 self
.csr
.add_region(name
+ "_" + memory
.name_override
, SoCCSRRegion(
878 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
879 busword
= self
.csr
.data_width
,
882 # Sort CSR regions by origin
883 self
.csr
.regions
= {k
: v
for k
, v
in sorted(self
.csr
.regions
.items(), key
=lambda item
: item
[1].origin
)}
885 # Add CSRs / Config items to constants
886 for name
, constant
in self
.csr_bankarray
.constants
:
887 self
.add_constant(name
+ "_" + constant
.name
, constant
.value
.value
)
889 # SoC CPU Check ----------------------------------------------------------------------------
890 if not isinstance(self
.cpu
, cpu
.CPUNone
):
891 if "sram" not in self
.bus
.regions
.keys():
892 self
.logger
.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
894 colorer("defined", color
="red")))
895 self
.logger
.error(self
.bus
)
897 cpu_reset_address_valid
= False
898 for name
, container
in self
.bus
.regions
.items():
899 if self
.bus
.check_region_is_in(
900 region
= SoCRegion(origin
=self
.cpu
.reset_address
, size
=self
.bus
.data_width
//8),
901 container
= container
):
902 cpu_reset_address_valid
= True
904 self
.cpu
.use_rom
= True
905 if not cpu_reset_address_valid
:
906 self
.logger
.error("CPU needs {} to be in a {} Region.".format(
907 colorer("reset address 0x{:08x}".format(self
.cpu
.reset_address
)),
908 colorer("defined", color
="red")))
909 self
.logger
.error(self
.bus
)
912 # SoC IRQ Interconnect ---------------------------------------------------------------------
913 if hasattr(self
, "cpu"):
914 if hasattr(self
.cpu
, "interrupt"):
915 for name
, loc
in sorted(self
.irq
.locs
.items()):
916 if name
in self
.cpu
.interrupts
.keys():
918 if hasattr(self
, name
):
919 module
= getattr(self
, name
)
920 if not hasattr(module
, "ev"):
921 self
.logger
.error("EventManager {} in {} SubModule.".format(
922 colorer("not found", color
="red"),
925 self
.comb
+= self
.cpu
.interrupt
[loc
].eq(module
.ev
.irq
)
926 self
.add_constant(name
+ "_INTERRUPT", loc
)
928 # SoC build ------------------------------------------------------------------------------------
929 def build(self
, *args
, **kwargs
):
930 self
.build_name
= kwargs
.pop("build_name", self
.platform
.name
)
931 kwargs
.update({"build_name": self
.build_name
})
932 return self
.platform
.build(self
, *args
, **kwargs
)
934 # LiteXSoC -----------------------------------------------------------------------------------------
937 # Add Identifier -------------------------------------------------------------------------------
938 def add_identifier(self
, name
="identifier", identifier
="LiteX SoC", with_build_time
=True):
939 self
.check_if_exists(name
)
941 identifier
+= " " + build_time()
942 setattr(self
.submodules
, name
, Identifier(identifier
))
943 self
.csr
.add(name
+ "_mem", use_loc_if_exists
=True)
945 # Add UART -------------------------------------------------------------------------------------
946 def add_uart(self
, name
, baudrate
=115200, fifo_depth
=16):
947 from litex
.soc
.cores
import uart
950 if name
in ["stub", "stream"]:
951 self
.submodules
.uart
= uart
.UART(tx_fifo_depth
=0, rx_fifo_depth
=0)
953 self
.comb
+= self
.uart
.sink
.ready
.eq(1)
956 elif name
in ["uartbone", "bridge"]:
957 self
.add_uartbone(baudrate
=baudrate
)
960 elif name
in ["crossover"]:
961 self
.submodules
.uart
= uart
.UARTCrossover()
964 elif name
in ["model", "sim"]:
965 self
.submodules
.uart_phy
= uart
.RS232PHYModel(self
.platform
.request("serial"))
966 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
,
967 tx_fifo_depth
= fifo_depth
,
968 rx_fifo_depth
= fifo_depth
))
971 elif name
in ["jtag_atlantic"]:
972 from litex
.soc
.cores
.jtag
import JTAGAtlantic
973 self
.submodules
.uart_phy
= JTAGAtlantic()
974 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
,
975 tx_fifo_depth
= fifo_depth
,
976 rx_fifo_depth
= fifo_depth
))
979 elif name
in ["jtag_uart"]:
980 from litex
.soc
.cores
.jtag
import JTAGPHY
981 self
.submodules
.uart_phy
= JTAGPHY(device
=self
.platform
.device
)
982 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
,
983 tx_fifo_depth
= fifo_depth
,
984 rx_fifo_depth
= fifo_depth
))
986 # USB ACM (with ValentyUSB core)
987 elif name
in ["usb_acm"]:
988 import valentyusb
.usbcore
.io
as usbio
989 import valentyusb
.usbcore
.cpu
.cdc_eptri
as cdc_eptri
990 usb_pads
= self
.platform
.request("usb")
991 usb_iobuf
= usbio
.IoBuf(usb_pads
.d_p
, usb_pads
.d_n
, usb_pads
.pullup
)
992 self
.submodules
.uart
= cdc_eptri
.CDCUsb(usb_iobuf
)
996 self
.submodules
.uart_phy
= uart
.UARTPHY(
997 pads
= self
.platform
.request(name
),
998 clk_freq
= self
.sys_clk_freq
,
1000 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
,
1001 tx_fifo_depth
= fifo_depth
,
1002 rx_fifo_depth
= fifo_depth
))
1004 self
.csr
.add("uart_phy", use_loc_if_exists
=True)
1005 self
.csr
.add("uart", use_loc_if_exists
=True)
1006 if hasattr(self
.cpu
, "interrupt"):
1007 self
.irq
.add("uart", use_loc_if_exists
=True)
1009 self
.add_constant("UART_POLLING")
1011 # Add UARTbone ---------------------------------------------------------------------------------
1012 def add_uartbone(self
, name
="serial", baudrate
=115200):
1013 from litex
.soc
.cores
import uart
1014 self
.submodules
.uartbone
= uart
.UARTBone(
1015 pads
= self
.platform
.request(name
),
1016 clk_freq
= self
.sys_clk_freq
,
1017 baudrate
= baudrate
)
1018 self
.bus
.add_master(name
="uartbone", master
=self
.uartbone
.wishbone
)
1020 # Add SDRAM ------------------------------------------------------------------------------------
1021 def add_sdram(self
, name
, phy
, module
, origin
, size
=None, with_soc_interconnect
=True,
1022 l2_cache_size
= 8192,
1023 l2_cache_min_data_width
= 128,
1024 l2_cache_reverse
= True,
1025 l2_cache_full_memory_we
= True,
1029 from litedram
.common
import LiteDRAMNativePort
1030 from litedram
.core
import LiteDRAMCore
1031 from litedram
.frontend
.wishbone
import LiteDRAMWishbone2Native
1032 from litedram
.frontend
.axi
import LiteDRAMAXI2Native
1035 self
.submodules
.sdram
= LiteDRAMCore(
1037 geom_settings
= module
.geom_settings
,
1038 timing_settings
= module
.timing_settings
,
1039 clk_freq
= self
.sys_clk_freq
,
1041 self
.csr
.add("sdram")
1043 if not with_soc_interconnect
: return
1045 # Compute/Check SDRAM size
1046 sdram_size
= 2**(module
.geom_settings
.bankbits
+
1047 module
.geom_settings
.rowbits
+
1048 module
.geom_settings
.colbits
)*phy
.settings
.databits
//8
1049 if size
is not None:
1050 sdram_size
= min(sdram_size
, size
)
1053 self
.bus
.add_region("main_ram", SoCRegion(origin
=origin
, size
=sdram_size
))
1055 # SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
1056 if len(self
.cpu
.memory_buses
):
1057 # When CPU has at least a direct memory bus, connect them directly to LiteDRAM.
1058 for mem_bus
in self
.cpu
.memory_buses
:
1059 # Request a LiteDRAM native port.
1060 port
= self
.sdram
.crossbar
.get_port()
1061 port
.data_width
= 2**int(log2(port
.data_width
)) # Round to nearest power of 2.
1063 # Check if bus is an AXI bus and connect it.
1064 if isinstance(mem_bus
, axi
.AXIInterface
):
1065 # If same data_width, connect it directly.
1066 if port
.data_width
== mem_bus
.data_width
:
1067 self
.logger
.info("Matching AXI MEM data width ({})\n".format(port
.data_width
))
1068 self
.submodules
+= LiteDRAMAXI2Native(
1069 axi
= self
.cpu
.mem_axi
,
1071 base_address
= self
.bus
.regions
["main_ram"].origin
)
1072 # If different data_width, do the adaptation and connect it via Wishbone.
1074 self
.logger
.info("Converting MEM data width: {} to {} via Wishbone".format(
1076 self
.cpu
.mem_axi
.data_width
))
1077 # FIXME: replace WB data-width converter with native AXI converter!!!
1078 mem_wb
= wishbone
.Interface(
1079 data_width
= self
.cpu
.mem_axi
.data_width
,
1080 adr_width
= 32-log2_int(self
.cpu
.mem_axi
.data_width
//8))
1081 # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
1082 mem_a2w
= ResetInserter()(axi
.AXI2Wishbone(
1083 axi
= self
.cpu
.mem_axi
,
1086 self
.comb
+= mem_a2w
.reset
.eq(ResetSignal() | self
.cpu
.reset
)
1087 self
.submodules
+= mem_a2w
1088 litedram_wb
= wishbone
.Interface(port
.data_width
)
1089 self
.submodules
+= LiteDRAMWishbone2Native(
1090 wishbone
= litedram_wb
,
1092 base_address
= origin
)
1093 self
.submodules
+= wishbone
.Converter(mem_wb
, litedram_wb
)
1094 # Check if bus is a Native bus and connect it.
1095 if isinstance(mem_bus
, LiteDRAMNativePort
):
1096 # If same data_width, connect it directly.
1097 if port
.data_width
== mem_bus
.data_width
:
1098 self
.comb
+= mem_bus
.cmd
.connect(port
.cmd
)
1099 self
.comb
+= mem_bus
.wdata
.connect(port
.wdata
)
1100 self
.comb
+= port
.rdata
.connect(mem_bus
.rdata
)
1103 raise NotImplementedError
1105 # When CPU has no direct memory interface, create a Wishbone Slave interface to LiteDRAM.
1107 # Request a LiteDRAM native port.
1108 port
= self
.sdram
.crossbar
.get_port()
1109 port
.data_width
= 2**int(log2(port
.data_width
)) # Round to nearest power of 2.
1111 # Create Wishbone Slave.
1112 wb_sdram
= wishbone
.Interface()
1113 self
.bus
.add_slave("main_ram", wb_sdram
)
1116 if l2_cache_size
!= 0:
1117 # Insert L2 cache inbetween Wishbone bus and LiteDRAM
1118 l2_cache_size
= max(l2_cache_size
, int(2*port
.data_width
/8)) # Use minimal size if lower
1119 l2_cache_size
= 2**int(log2(l2_cache_size
)) # Round to nearest power of 2
1120 l2_cache_data_width
= max(port
.data_width
, l2_cache_min_data_width
)
1121 l2_cache
= wishbone
.Cache(
1122 cachesize
= l2_cache_size
//4,
1124 slave
= wishbone
.Interface(l2_cache_data_width
),
1125 reverse
= l2_cache_reverse
)
1126 if l2_cache_full_memory_we
:
1127 l2_cache
= FullMemoryWE()(l2_cache
)
1128 self
.submodules
.l2_cache
= l2_cache
1129 litedram_wb
= self
.l2_cache
.slave
1131 litedram_wb
= wishbone
.Interface(port
.data_width
)
1132 self
.submodules
+= wishbone
.Converter(wb_sdram
, litedram_wb
)
1133 self
.add_config("L2_SIZE", l2_cache_size
)
1135 # Wishbone Slave <--> LiteDRAM bridge
1136 self
.submodules
.wishbone_bridge
= LiteDRAMWishbone2Native(litedram_wb
, port
,
1137 base_address
= self
.bus
.regions
["main_ram"].origin
)
1139 # Add Ethernet ---------------------------------------------------------------------------------
1140 def add_ethernet(self
, name
="ethmac", phy
=None):
1142 from liteeth
.mac
import LiteEthMAC
1144 ethmac
= LiteEthMAC(
1147 interface
= "wishbone",
1148 endianness
= self
.cpu
.endianness
)
1149 setattr(self
.submodules
, name
, ethmac
)
1150 ethmac_region
= SoCRegion(origin
=self
.mem_map
.get(name
, None), size
=0x2000, cached
=False)
1151 self
.bus
.add_slave(name
=name
, slave
=ethmac
.bus
, region
=ethmac_region
)
1153 self
.add_interrupt(name
)
1154 # Timing constraints
1155 if hasattr(phy
, "crg"):
1156 eth_rx_clk
= phy
.crg
.cd_eth_rx
.clk
1157 eth_tx_clk
= phy
.crg
.cd_eth_tx
.clk
1159 eth_rx_clk
= phy
.cd_eth_rx
.clk
1160 eth_tx_clk
= phy
.cd_eth_tx
.clk
1161 self
.platform
.add_period_constraint(eth_rx_clk
, 1e9
/phy
.rx_clk_freq
)
1162 self
.platform
.add_period_constraint(eth_tx_clk
, 1e9
/phy
.tx_clk_freq
)
1163 self
.platform
.add_false_path_constraints(
1164 self
.crg
.cd_sys
.clk
,
1168 # Add Etherbone --------------------------------------------------------------------------------
1169 def add_etherbone(self
, name
="etherbone", phy
=None, clock_domain
=None,
1170 mac_address
= 0x10e2d5000000,
1171 ip_address
= "192.168.1.50",
1174 from liteeth
.core
import LiteEthUDPIPCore
1175 from liteeth
.frontend
.etherbone
import LiteEthEtherbone
1177 ethcore
= LiteEthUDPIPCore(
1179 mac_address
= mac_address
,
1180 ip_address
= ip_address
,
1181 clk_freq
= self
.clk_freq
)
1182 if clock_domain
is not None: # FIXME: Could probably be avoided.
1183 ethcore
= ClockDomainsRenamer("eth_tx")(ethcore
)
1184 self
.submodules
+= ethcore
1186 # Clock domain renaming
1187 if clock_domain
is not None: # FIXME: Could probably be avoided.
1188 self
.clock_domains
.cd_etherbone
= ClockDomain("etherbone")
1189 self
.comb
+= self
.cd_etherbone
.clk
.eq(ClockSignal(clock_domain
))
1190 self
.comb
+= self
.cd_etherbone
.rst
.eq(ResetSignal(clock_domain
))
1191 clock_domain
= "etherbone"
1193 clock_domain
= "sys"
1196 etherbone
= LiteEthEtherbone(ethcore
.udp
, udp_port
, cd
=clock_domain
)
1197 setattr(self
.submodules
, name
, etherbone
)
1198 self
.add_wb_master(etherbone
.wishbone
.bus
)
1199 # Timing constraints
1200 if hasattr(phy
, "crg"):
1201 eth_rx_clk
= phy
.crg
.cd_eth_rx
.clk
1202 eth_tx_clk
= phy
.crg
.cd_eth_tx
.clk
1204 eth_rx_clk
= phy
.cd_eth_rx
.clk
1205 eth_tx_clk
= phy
.cd_eth_tx
.clk
1206 self
.platform
.add_period_constraint(eth_rx_clk
, 1e9
/phy
.rx_clk_freq
)
1207 self
.platform
.add_period_constraint(eth_tx_clk
, 1e9
/phy
.tx_clk_freq
)
1208 self
.platform
.add_false_path_constraints(
1209 self
.crg
.cd_sys
.clk
,
1213 # Add SPI Flash --------------------------------------------------------------------------------
1214 def add_spi_flash(self
, name
="spiflash", mode
="4x", dummy_cycles
=None, clk_freq
=None):
1215 assert dummy_cycles
is not None # FIXME: Get dummy_cycles from SPI Flash
1216 assert mode
in ["1x", "4x"]
1217 if clk_freq
is None: clk_freq
= self
.clk_freq
/2 # FIXME: Get max clk_freq from SPI Flash
1218 spiflash
= SpiFlash(
1219 pads
= self
.platform
.request(name
if mode
== "1x" else name
+ mode
),
1220 dummy
= dummy_cycles
,
1221 div
= ceil(self
.clk_freq
/clk_freq
),
1222 with_bitbang
= True,
1223 endianness
= self
.cpu
.endianness
)
1224 spiflash
.add_clk_primitive(self
.platform
.device
)
1225 setattr(self
.submodules
, name
, spiflash
)
1226 self
.add_memory_region(name
, self
.mem_map
[name
], 0x1000000) # FIXME: Get size from SPI Flash
1227 self
.add_wb_slave(self
.mem_map
[name
], spiflash
.bus
)
1230 # Add SPI SDCard -------------------------------------------------------------------------------
1231 def add_spi_sdcard(self
, name
="spisdcard", clk_freq
=400e3
):
1232 pads
= self
.platform
.request(name
)
1233 if hasattr(pads
, "rst"):
1234 self
.comb
+= pads
.rst
.eq(0)
1235 spisdcard
= SPIMaster(pads
, 8, self
.sys_clk_freq
, 400e3
)
1236 spisdcard
.add_clk_divider()
1237 setattr(self
.submodules
, name
, spisdcard
)