|
| 1 | +# |
| 2 | +# This file is part of LiteX. |
| 3 | +# |
| 4 | +# Copyright (c) 2020 Antmicro <www.antmicro.com> |
| 5 | +# SPDX-License-Identifier: BSD-2-Clause |
| 6 | + |
| 7 | +import os |
| 8 | +import re |
| 9 | + |
| 10 | +from migen import * |
| 11 | +from migen.fhdl.specials import Tristate |
| 12 | + |
| 13 | +from litex import get_data_mod |
| 14 | +from litex.soc.interconnect import wishbone, stream |
| 15 | +from litex.soc.interconnect.csr import * |
| 16 | +from litex.soc.cores.cpu import CPU, CPU_GCC_TRIPLE_RISCV32 |
| 17 | + |
| 18 | +# Variants ----------------------------------------------------------------------------------------- |
| 19 | + |
| 20 | +CPU_VARIANTS = ["standard"] |
| 21 | + |
| 22 | +# GCC Flags ---------------------------------------------------------------------------------------- |
| 23 | + |
| 24 | +GCC_FLAGS = { |
| 25 | + # /-------- Base ISA |
| 26 | + # |/------- Hardware Multiply + Divide |
| 27 | + # ||/----- Atomics |
| 28 | + # |||/---- Compressed ISA |
| 29 | + # ||||/--- Single-Precision Floating-Point |
| 30 | + # |||||/-- Double-Precision Floating-Point |
| 31 | + # imacfd |
| 32 | + "standard": "-march=rv32imc -mabi=ilp32 ", |
| 33 | +} |
| 34 | + |
| 35 | +# OBI / APB / Trace Layouts ------------------------------------------------------------------------ |
| 36 | + |
| 37 | +obi_layout = [ |
| 38 | + ("req", 1), |
| 39 | + ("gnt", 1), |
| 40 | + ("addr", 32), |
| 41 | + ("we", 1), |
| 42 | + ("be", 4), |
| 43 | + ("wdata", 32), |
| 44 | + ("rvalid", 1), |
| 45 | + ("rdata", 32), |
| 46 | +] |
| 47 | + |
| 48 | +apb_layout = [ |
| 49 | + ("paddr", 32), |
| 50 | + ("pwdata", 32), |
| 51 | + ("pwrite", 1), |
| 52 | + ("psel", 1), |
| 53 | + ("penable", 1), |
| 54 | + ("prdata", 32), |
| 55 | + ("pready", 1), |
| 56 | + ("pslverr", 1), |
| 57 | +] |
| 58 | + |
| 59 | +# Helpers ------------------------------------------------------------------------------------------ |
| 60 | + |
| 61 | +def add_manifest_sources(platform, manifest): |
| 62 | + basedir = get_data_mod("cpu", "cv32e41p").data_location |
| 63 | + with open(os.path.join(basedir, manifest), 'r') as f: |
| 64 | + for l in f: |
| 65 | + res = re.search('\$\{DESIGN_RTL_DIR\}/(.+)', l) |
| 66 | + if res and not re.match('//', l): |
| 67 | + if re.match('\+incdir\+', l): |
| 68 | + platform.add_verilog_include_path(os.path.join(basedir, 'rtl', res.group(1))) |
| 69 | + else: |
| 70 | + platform.add_source(os.path.join(basedir, 'rtl', res.group(1))) |
| 71 | + |
| 72 | +# OBI <> Wishbone ---------------------------------------------------------------------------------- |
| 73 | + |
| 74 | +class OBI2Wishbone(Module): |
| 75 | + def __init__(self, obi, wb): |
| 76 | + addr = Signal.like(obi.addr) |
| 77 | + be = Signal.like(obi.be) |
| 78 | + we = Signal.like(obi.we) |
| 79 | + wdata = Signal.like(obi.wdata) |
| 80 | + |
| 81 | + self.submodules.fsm = fsm = FSM(reset_state="IDLE") |
| 82 | + fsm.act("IDLE", |
| 83 | + # On OBI request: |
| 84 | + If(obi.req, |
| 85 | + # Drive Wishbone bus from OBI bus. |
| 86 | + wb.adr.eq(obi.addr[2:32]), |
| 87 | + wb.stb.eq( 1), |
| 88 | + wb.dat_w.eq( obi.wdata), |
| 89 | + wb.cyc.eq( 1), |
| 90 | + wb.sel.eq( obi.be), |
| 91 | + wb.we.eq( obi.we), |
| 92 | + |
| 93 | + # Store OBI bus values. |
| 94 | + NextValue(addr, obi.addr), |
| 95 | + NextValue(be, obi.be), |
| 96 | + NextValue(we, obi.we), |
| 97 | + NextValue(wdata, obi.wdata), |
| 98 | + |
| 99 | + # Now we need to wait Wishbone Ack. |
| 100 | + NextState("ACK") |
| 101 | + ), |
| 102 | + obi.gnt.eq(1), # Always ack OBI request in Idle. |
| 103 | + ) |
| 104 | + fsm.act("ACK", |
| 105 | + # Drive Wishbone bus from stored OBI bus values. |
| 106 | + wb.adr.eq(addr[2:32]), |
| 107 | + wb.stb.eq( 1), |
| 108 | + wb.dat_w.eq( wdata), |
| 109 | + wb.cyc.eq( 1), |
| 110 | + wb.sel.eq( be), |
| 111 | + wb.we.eq( we), |
| 112 | + |
| 113 | + # On Wishbone Ack: |
| 114 | + If(wb.ack, |
| 115 | + # Generate OBI response. |
| 116 | + obi.rvalid.eq(1), |
| 117 | + obi.rdata.eq(wb.dat_r), |
| 118 | + |
| 119 | + # Return to Idle. |
| 120 | + NextState("IDLE") |
| 121 | + ) |
| 122 | + ) |
| 123 | + |
| 124 | +class Wishbone2OBI(Module): |
| 125 | + def __init__(self, wb, obi): |
| 126 | + self.submodules.fsm = fsm = FSM(reset_state="IDLE") |
| 127 | + fsm.act("IDLE", |
| 128 | + If(wb.cyc & wb.stb, |
| 129 | + obi.req.eq(1), |
| 130 | + NextState("ACK"), |
| 131 | + ) |
| 132 | + ) |
| 133 | + fsm.act("ACK", |
| 134 | + wb.ack.eq(1), |
| 135 | + NextState("IDLE"), |
| 136 | + ) |
| 137 | + |
| 138 | + self.comb += [ |
| 139 | + obi.we.eq(wb.we), |
| 140 | + obi.be.eq(wb.sel), |
| 141 | + obi.addr.eq(Cat(Signal(2), wb.adr)), |
| 142 | + obi.wdata.eq(wb.dat_w), |
| 143 | + wb.dat_r.eq(obi.rdata), |
| 144 | + ] |
| 145 | + |
| 146 | +# Wishbone <> APB ---------------------------------------------------------------------------------- |
| 147 | + |
| 148 | +class Wishbone2APB(Module): |
| 149 | + def __init__(self, wb, apb): |
| 150 | + self.submodules.fsm = fsm = FSM(reset_state="IDLE") |
| 151 | + fsm.act("IDLE", |
| 152 | + If(wb.cyc & wb.stb, |
| 153 | + NextState("ACK"), |
| 154 | + ) |
| 155 | + ) |
| 156 | + fsm.act("ACK", |
| 157 | + apb.penable.eq(1), |
| 158 | + wb.ack.eq(1), |
| 159 | + NextState("IDLE"), |
| 160 | + ) |
| 161 | + |
| 162 | + self.comb += [ |
| 163 | + apb.paddr.eq(Cat(Signal(2), wb.adr)), |
| 164 | + apb.pwrite.eq(wb.we), |
| 165 | + apb.psel.eq(1), |
| 166 | + apb.pwdata.eq(wb.dat_w), |
| 167 | + wb.dat_r.eq(apb.prdata), |
| 168 | + ] |
| 169 | + |
| 170 | +# Debug Module ------------------------------------------------------------------------------------- |
| 171 | + |
| 172 | +class DebugModule(Module): |
| 173 | + jtag_layout = [ |
| 174 | + ("tck", 1), |
| 175 | + ("tms", 1), |
| 176 | + ("trst", 1), |
| 177 | + ("tdi", 1), |
| 178 | + ("tdo", 1), |
| 179 | + ] |
| 180 | + def __init__(self, pads=None): |
| 181 | + if pads is None: |
| 182 | + pads = Record(self.jtag_layout) |
| 183 | + self.pads = pads |
| 184 | + self.dmbus = wishbone.Interface() |
| 185 | + self.sbbus = wishbone.Interface() |
| 186 | + dmbus = Record(obi_layout) |
| 187 | + sbbus = Record(obi_layout) |
| 188 | + |
| 189 | + self.submodules.sbbus_conv = OBI2Wishbone(sbbus, self.sbbus) |
| 190 | + self.submodules.dmbus_conv = Wishbone2OBI(self.dmbus, dmbus) |
| 191 | + |
| 192 | + self.debug_req = Signal() |
| 193 | + self.ndmreset = Signal() |
| 194 | + |
| 195 | + tdo_i = Signal() |
| 196 | + tdo_o = Signal() |
| 197 | + tdo_oe = Signal() |
| 198 | + |
| 199 | + self.specials += Tristate(pads.tdo, tdo_o, tdo_oe, tdo_i) |
| 200 | + |
| 201 | + self.dm_params = dict( |
| 202 | + # Clk / Rst. |
| 203 | + i_clk = ClockSignal("sys"), |
| 204 | + i_rst_n = ~ResetSignal("sys"), |
| 205 | + o_ndmreset = self.ndmreset, |
| 206 | + o_debug_req = self.debug_req, |
| 207 | + |
| 208 | + # Slave Bus. |
| 209 | + i_dm_req = dmbus.req, |
| 210 | + i_dm_we = dmbus.we, |
| 211 | + i_dm_addr = dmbus.addr, |
| 212 | + i_dm_be = dmbus.be, |
| 213 | + i_dm_wdata = dmbus.wdata, |
| 214 | + o_dm_rdata = dmbus.rdata, |
| 215 | + |
| 216 | + # Master Bus. |
| 217 | + o_sb_req = sbbus.req, |
| 218 | + o_sb_addr = sbbus.addr, |
| 219 | + o_sb_we = sbbus.we, |
| 220 | + o_sb_wdata = sbbus.wdata, |
| 221 | + o_sb_be = sbbus.be, |
| 222 | + i_sb_gnt = sbbus.gnt, |
| 223 | + i_sb_rvalid = sbbus.rvalid, |
| 224 | + i_sb_rdata = sbbus.rdata, |
| 225 | + |
| 226 | + # JTAG. |
| 227 | + i_tck = pads.tck, |
| 228 | + i_tms = pads.tms, |
| 229 | + i_trst_n = pads.trst, |
| 230 | + i_tdi = pads.tdi, |
| 231 | + o_tdo = tdo_o, |
| 232 | + o_tdo_oe = tdo_oe, |
| 233 | + ) |
| 234 | + |
| 235 | + self.comb += [ |
| 236 | + dmbus.gnt.eq(dmbus.req), |
| 237 | + dmbus.rvalid.eq(dmbus.gnt), |
| 238 | + ] |
| 239 | + |
| 240 | + self.specials += Instance("dm_wrap", **self.dm_params) |
| 241 | + |
| 242 | +# CV32E41P ----------------------------------------------------------------------------------------- |
| 243 | + |
| 244 | +class CV32E41P(CPU): |
| 245 | + family = "riscv" |
| 246 | + name = "cv32e41p" |
| 247 | + human_name = "CV32E41P" |
| 248 | + variants = CPU_VARIANTS |
| 249 | + data_width = 32 |
| 250 | + endianness = "little" |
| 251 | + gcc_triple = CPU_GCC_TRIPLE_RISCV32 |
| 252 | + linker_output_format = "elf32-littleriscv" |
| 253 | + nop = "nop" |
| 254 | + io_regions = {0x80000000: 0x80000000} # Origin, Length. |
| 255 | + |
| 256 | + # GCC Flags. |
| 257 | + @property |
| 258 | + def gcc_flags(self): |
| 259 | + flags = GCC_FLAGS[self.variant] |
| 260 | + flags += "-D__cv32e41p__ " |
| 261 | + return flags |
| 262 | + |
| 263 | + def __init__(self, platform, variant="standard"): |
| 264 | + self.platform = platform |
| 265 | + self.variant = variant |
| 266 | + self.reset = Signal() |
| 267 | + self.ibus = wishbone.Interface() |
| 268 | + self.dbus = wishbone.Interface() |
| 269 | + self.periph_buses = [self.ibus, self.dbus] |
| 270 | + self.memory_buses = [] |
| 271 | + self.interrupt = Signal(16) |
| 272 | + self.interrupt_padding = Signal(16) |
| 273 | + |
| 274 | + ibus = Record(obi_layout) |
| 275 | + dbus = Record(obi_layout) |
| 276 | + |
| 277 | + # OBI <> Wishbone. |
| 278 | + self.submodules.ibus_conv = OBI2Wishbone(ibus, self.ibus) |
| 279 | + self.submodules.dbus_conv = OBI2Wishbone(dbus, self.dbus) |
| 280 | + |
| 281 | + self.comb += [ |
| 282 | + ibus.we.eq(0), |
| 283 | + ibus.be.eq(1111), |
| 284 | + ] |
| 285 | + |
| 286 | + self.cpu_params = dict( |
| 287 | + # Clk / Rst. |
| 288 | + i_clk_i = ClockSignal("sys"), |
| 289 | + i_rst_ni = ~ResetSignal("sys"), |
| 290 | + |
| 291 | + # Controls. |
| 292 | + i_pulp_clock_en_i = 1, |
| 293 | + i_scan_cg_en_i = 0, |
| 294 | + i_mtvec_addr_i = 0, |
| 295 | + i_dm_halt_addr_i = 0, |
| 296 | + i_hart_id_i = 0, |
| 297 | + i_dm_exception_addr_i = 0, |
| 298 | + |
| 299 | + # IBus. |
| 300 | + o_instr_req_o = ibus.req, |
| 301 | + i_instr_gnt_i = ibus.gnt, |
| 302 | + i_instr_rvalid_i = ibus.rvalid, |
| 303 | + o_instr_addr_o = ibus.addr, |
| 304 | + i_instr_rdata_i = ibus.rdata, |
| 305 | + |
| 306 | + # DBus. |
| 307 | + o_data_req_o = dbus.req, |
| 308 | + i_data_gnt_i = dbus.gnt, |
| 309 | + i_data_rvalid_i = dbus.rvalid, |
| 310 | + o_data_we_o = dbus.we, |
| 311 | + o_data_be_o = dbus.be, |
| 312 | + o_data_addr_o = dbus.addr, |
| 313 | + o_data_wdata_o = dbus.wdata, |
| 314 | + i_data_rdata_i = dbus.rdata, |
| 315 | + |
| 316 | + # APU. |
| 317 | + i_apu_gnt_i = 0, |
| 318 | + i_apu_rvalid_i = 0, |
| 319 | + |
| 320 | + # IRQ. |
| 321 | + i_irq_i = Cat(self.interrupt_padding,self.interrupt), |
| 322 | + |
| 323 | + # Debug. |
| 324 | + i_debug_req_i = 0, |
| 325 | + |
| 326 | + # CPU Control. |
| 327 | + i_fetch_enable_i = 1, |
| 328 | + ) |
| 329 | + |
| 330 | + # Add Verilog sources. |
| 331 | + add_manifest_sources(platform, 'cv32e41p_manifest.flist') |
| 332 | + |
| 333 | + def add_debug_module(self, dm): |
| 334 | + self.cpu_params.update(i_debug_req_i=dm.debug_req) |
| 335 | + self.cpu_params.update(i_rst_ni=~(ResetSignal() | dm.ndmreset)) |
| 336 | + |
| 337 | + def set_reset_address(self, reset_address): |
| 338 | + self.reset_address = reset_address |
| 339 | + self.cpu_params.update(i_boot_addr_i=Signal(32, reset=reset_address)) |
| 340 | + |
| 341 | + def do_finalize(self): |
| 342 | + assert hasattr(self, "reset_address") |
| 343 | + self.specials += Instance("cv32e41p_core", **self.cpu_params) |
0 commit comments