tidyup on gramWishbone class, add comments
[gram.git] / gram / frontend / wishbone.py
1 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
2 # License: BSD
3 # Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
4 # Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
5 #
6 # Code from LambaConcept is Licensed BSD
7 # Code from Luke Kenneth Casson Leighton is Licensed LGPLv3+
8 #
9 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
10 # under EU Grants 871528 and 957073
11
12 from math import log2
13
14 from nmigen import (Module, Elaboratable, Signal, Repl)
15 from nmigen.utils import log2_int
16
17 from nmigen_soc import wishbone
18 from nmigen_soc.memory import MemoryMap
19 from lambdasoc.periph import Peripheral
20
21 # XXX
22 # WARNING - THIS CODE CANNOT COPE WITH WISHBONE 4.0 PIPELINE MODE
23 # THE ADDRESS MAY CHANGE AFTER EACH STB AND THIS IS AN ASSUMPTION
24 # FROM WISHBONE 3.0 CLASSIC. USE THE COMPATIBILITY MODE stall=cyc&~ack
25 # OR USE BURST-MODE ONLY
26 # XXX
27 class gramWishbone(Peripheral, Elaboratable):
28 def __init__(self, core, data_width=32, granularity=8,
29 features=frozenset()):
30
31 super().__init__(name="wishbone")
32
33 self.native_port = core.crossbar.get_native_port()
34
35 self.ratio = self.native_port.data_width//data_width
36 addr_width = log2_int(core.size//self.ratio)
37 addr_width_r = addr_width + log2_int(self.ratio)
38 self.dsize = log2_int(data_width//granularity)
39 self.bus = wishbone.Interface(addr_width=addr_width_r,
40 data_width=data_width,
41 granularity=granularity,
42 features=features)
43
44 mmap = MemoryMap(addr_width=addr_width_r+self.dsize,
45 data_width=granularity)
46
47 self.bus.memory_map = mmap
48
49 def elaborate(self, platform):
50 m = Module()
51 comb = m.d.comb
52 cmd = self.native_port.cmd
53 wdata = self.native_port.wdata
54 rdata = self.native_port.rdata
55 bus = self.bus
56
57 # Write datapath
58 comb += wdata.valid.eq(bus.cyc & bus.stb & bus.we)
59
60 ratio_bitmask = Repl(1, log2_int(self.ratio))
61
62 # XXX? sel is zero being compensated-for as all 1s does not seem right
63 sel = Signal.like(bus.sel)
64 with m.If(bus.sel == 0):
65 comb += sel.eq(-1) # all 1s
66 with m.Else():
67 comb += sel.eq(bus.sel)
68
69 with m.Switch(bus.adr & ratio_bitmask): # XXX adr changes (WB4-pipe)
70 for i in range(self.ratio):
71 with m.Case(i):
72 # write-enable
73 we = Repl(sel, bus.granularity//8) << (self.ratio*i)
74 comb += wdata.we.eq(we)
75 # write-data
76 data = bus.dat_w << (bus.data_width*i)
77 comb += wdata.data.eq(data)
78
79 # Read datapath
80 comb += rdata.ready.eq(1)
81
82 with m.Switch(bus.adr & ratio_bitmask): # XXX adr changes (WB4-pipe)
83 for i in range(self.ratio):
84 with m.Case(i):
85 data = rdata.data >> (bus.data_width*i)
86 comb += bus.dat_r.eq(data)
87
88 # Command FSM
89 with m.FSM():
90 # raise a command when WB has a request
91 with m.State("Send-Cmd"):
92 # XXX this logic is only WB 3.0 classic compatible!
93 comb += [
94 cmd.valid.eq(bus.cyc & bus.stb),
95 cmd.we.eq(bus.we),
96 cmd.addr.eq(bus.adr >> self.dsize),
97 ]
98
99 # when cmd is accepted, move to either read or write FSM
100 with m.If(cmd.valid & cmd.ready):
101 with m.If(bus.we):
102 m.next = "Wait-Write"
103 with m.Else():
104 m.next = "Wait-Read"
105
106 # read-wait: when read valid, ack the WB bus, return idle
107 with m.State("Wait-Read"):
108 with m.If(rdata.valid):
109 comb += bus.ack.eq(1)
110 m.next = "Send-Cmd"
111
112 # write-wait: when write valid, ack the WB bus, return idle
113 with m.State("Wait-Write"):
114 with m.If(wdata.ready):
115 comb += bus.ack.eq(1)
116 m.next = "Send-Cmd"
117
118 return m