diff --git a/litex/soc/cores/cpu/__init__.py b/litex/soc/cores/cpu/__init__.py index aad59ef42..22dd94aca 100644 --- a/litex/soc/cores/cpu/__init__.py +++ b/litex/soc/cores/cpu/__init__.py @@ -92,6 +92,8 @@ from litex.soc.cores.cpu.zynq7000 import Zynq7000 # EOS-S3 from litex.soc.cores.cpu.eos_s3 import EOS_S3 +from litex.soc.cores.cpu.gowin_emcu import GowinEMCU + CPUS = { # None "None" : CPUNone, @@ -127,4 +129,6 @@ CPUS = { # EOS-S3 "eos-s3" : EOS_S3, + + 'gowin_emcu' : GowinEMCU } diff --git a/litex/soc/cores/cpu/gowin_emcu/__init__.py b/litex/soc/cores/cpu/gowin_emcu/__init__.py new file mode 100644 index 000000000..28ef2fcc5 --- /dev/null +++ b/litex/soc/cores/cpu/gowin_emcu/__init__.py @@ -0,0 +1 @@ +from litex.soc.cores.cpu.gowin_emcu.core import GowinEMCU diff --git a/litex/soc/cores/cpu/gowin_emcu/core.py b/litex/soc/cores/cpu/gowin_emcu/core.py new file mode 100644 index 000000000..1fc88f363 --- /dev/null +++ b/litex/soc/cores/cpu/gowin_emcu/core.py @@ -0,0 +1,187 @@ +from migen import * +from litex.soc.interconnect import wishbone, ahb +from litex.soc.cores.cpu import CPU + + +class AHBFlash(Module): + def __init__(self, bus): + addr = Signal(13) + read_enable = Signal() + self.comb += bus.resp.eq(0) + self.submodules.fsm = fsm = FSM() + fsm.act("IDLE", + bus.readyout.eq(1), + If(bus.sel & bus.trans[1], + NextValue(addr, bus.addr[2:]), + NextState('READ'), + ) + ) + fsm.act("READ", + read_enable.eq(1), + NextState('WAIT'), + ) + fsm.act('WAIT', + NextState('IDLE') + ) + self.specials += Instance( + 'FLASH256K', + o_DOUT=bus.rdata, + i_DIN=Signal(32), + i_XADR=addr[6:], + i_YADR=addr[:6], + i_XE=~ResetSignal(), + i_YE=~ResetSignal(), + i_SE=read_enable, + i_PROG=0, + i_ERASE=0, + i_NVSTR=0 + ) + + +class GowinEMCU(CPU): + variants = ["standard"] + family = "arm" + name = "gowin_emcu" + human_name = "Gowin EMCU" + data_width = 32 + endianness = "little" + gcc_triple = "arm-none-eabi" + gcc_flags = '-mcpu=cortex-m3 -mthumb' + linker_output_format = "elf32-littlearm" + nop = "nop" + io_regions = {0x4000_0000: 0x2000_0000, + 0xA000_0000: 0x6000_0000} # Origin, Length. + + @property + def mem_map(self): + return { + "rom": 0x0000_0000, + "sram": 0x2000_0000, + "csr": 0xA000_0000, + } + + def __init__(self, platform, variant, *args, **kwargs): + super().__init__(*args, **kwargs) + self.reset = Signal() + self.bus_reset = Signal() + bus_reset_n = Signal() + self.comb += self.bus_reset.eq(~bus_reset_n) + self.interrupt = Signal(5) + self.reset_address = self.mem_map['rom'] + 0 + + self.gpio_in = Signal(16) + self.gpio_out = Signal(16) + self.gpio_out_en = Signal(16) + + self.cpu_params = dict() + self.cpu_params.update( + i_MTXREMAP=Signal(4, reset=0b1111), + o_MTXHRESETN=bus_reset_n, + + i_FLASHERR=Signal(), + i_FLASHINT=Signal(), + + i_FCLK=ClockSignal(), + i_PORESETN=~self.reset, + i_SYSRESETN=~self.reset, + i_RTCSRCCLK=Signal(), # TODO - RTC clk in + + i_IOEXPINPUTI=self.gpio_in, + o_IOEXPOUTPUTO=self.gpio_out, + o_IOEXPOUTPUTENO=self.gpio_out_en, + + i_GPINT=self.interrupt, + o_INTMONITOR=Signal(), + ) + + # 32b CPU SRAM split between 8 SRAMs x 4 bit each + + sram_dw = 32 + single_sram_dw = 4 + n_srams = sram_dw // single_sram_dw + + sram0_addr = Signal(13) + sram0_rdata = Signal(sram_dw) + sram0_wdata = Signal(sram_dw) + sram0_cs = Signal() + sram0_wren = Signal(4) + self.cpu_params.update( + i_SRAM0RDATA=sram0_rdata, + o_SRAM0ADDR=sram0_addr, + o_SRAM0WREN=sram0_wren, + o_SRAM0WDATA=sram0_wdata, + o_SRAM0CS=sram0_cs, + ) + + for i in range(n_srams): + self.specials += Instance( + 'SDPB', + p_READ_MODE=0, + p_BIT_WIDTH_0=single_sram_dw, + p_BIT_WIDTH_1=single_sram_dw, + p_RESET_MODE='SYNC', + p_BLK_SEL_0=0b111, + p_BLK_SEL_1=0b111, + o_DO=Cat(sram0_rdata[i * single_sram_dw: (i + 1) * single_sram_dw], Signal(sram_dw - single_sram_dw)), + i_DI=Cat(sram0_wdata[i * single_sram_dw: (i + 1) * single_sram_dw], Signal(sram_dw - single_sram_dw)), + i_ADA=Cat(Signal(2), sram0_addr[:-1]), + i_ADB=Cat(Signal(2), sram0_addr[:-1]), + i_CEA=sram0_wren[i // 2], + i_CEB=~sram0_wren[i // 2], + i_CLKA=ClockSignal(), + i_CLKB=ClockSignal(), + i_RESETA=0, + i_RESETB=self.bus_reset, + i_OCE=1, + i_BLKSELA=Cat(sram0_cs, sram0_cs, sram0_cs), + i_BLKSELB=Cat(sram0_cs, sram0_cs, sram0_cs), + ) + + # Boot Flash memory connected via AHB + + ahb_flash = ahb.Interface() + for s, _ in ahb_flash.master_signals: + if s in ['wdata', 'write', 'mastlock', 'prot']: + continue + self.cpu_params[f'o_TARGFLASH0H{s.upper()}'] = getattr(ahb_flash, s) + for s, _ in ahb_flash.slave_signals: + self.cpu_params[f'i_TARGFLASH0H{s.upper()}'] = getattr(ahb_flash, s) + flash = ResetInserter()(AHBFlash(ahb_flash)) + self.comb += flash.reset.eq(self.bus_reset) + self.submodules += flash + + # Extension AHB -> Wishbone CSR via bridge + + self.pbus = wishbone.Interface(data_width=32, adr_width=30) + self.periph_buses = [self.pbus] + ahb_targexp0 = ahb.Interface() + for s, _ in ahb_targexp0.master_signals: + # TODO: due to unexpected writes by the CPU bus is currently forced read-only + if s == 'write': + continue + self.cpu_params[f'o_TARGEXP0H{s.upper()}'] = getattr(ahb_targexp0, s) + for s, _ in ahb_targexp0.slave_signals: + self.cpu_params[f'i_TARGEXP0H{s.upper()}'] = getattr(ahb_targexp0, s) + self.submodules += ahb.AHB2Wishbone(ahb_targexp0, self.pbus) + + def connect_uart(self, pads, n=0): + assert n in (0, 1), "this CPU has 2 built-in UARTs, 0 and 1" + self.cpu_params.update({ + f'i_UART{n}RXDI': pads.rx, + f'o_UART{n}TXDO': pads.tx, + f'o_UART{n}BAUDTICK': Signal() + }) + + def connect_jtag(self, pads): + self.cpu_params.update( + i_DAPSWDITMS=pads.tms, + i_DAPTDI=pads.tdi, + o_DAPTDO=pads.tdo, + o_DAPNTDOEN=Signal(), + i_DAPNTRST=~self.reset, + i_DAPSWCLKTCK=pads.tck, + o_DAPJTAGNSW=Signal(), # indicates debug mode, JTAG or SWD + ) + + def do_finalize(self): + self.specials += Instance("EMCU", **self.cpu_params) diff --git a/litex/soc/integration/builder.py b/litex/soc/integration/builder.py index b8c385e8d..68369b4d0 100644 --- a/litex/soc/integration/builder.py +++ b/litex/soc/integration/builder.py @@ -275,7 +275,7 @@ class Builder: self.soc.platform.output_dir = self.output_dir # Check if BIOS is used and add software package if so. - with_bios = self.soc.cpu_type not in [None, "zynq7000", "eos-s3"] + with_bios = self.soc.cpu_type not in [None, "zynq7000", "eos-s3", 'gowin_emcu'] if with_bios: self.add_software_package("bios")