A look at MiST’s toplevel

Porting a core, DeMiSTified – Part 2 – 2021-02-25

The goal is to provide an environment in which a MiST core can be included as a submodule, thus porting the core to a new target board without making large-scale changes. To do this, we’re going to need a toplevel for each target board which maps the signals of MiST’s FPGA to board-specific resources.

Let’s take a close look at a MiST core’s toplevel…

module NES_mist(  
// clock input
input [1:0] CLOCK_27, // 27 MHz
output LED

The very first entry could be a problem – we have a clock input that’s two bits wide so very likely a differential clock. The target boards only have single-ended master clocks – but luckily I haven’t found a MiST core yet that uses more than just CLOCK_27[0]. Our target boards’ master clocks aren’t 27MHz either – but that’s not actually a problem, as will become clear in a future installment.

We have direct control over a single LED – we can map that to one of the target board’s LEDs easily enough.

Next we have video output:

   output         VGA_HS, // VGA H_SYNC
output VGA_VS, // VGA V_SYNC
output [ 5:0] VGA_R, // VGA Red[5:0]
output [ 5:0] VGA_G, // VGA Green[5:0]
output [ 5:0] VGA_B, // VGA Blue[5:0]

The MiST has a simple resistor ladder DAC for video with six bits per gun (it has this one, in fact!) The Turbo Chameleon 64 has only five bits per gun; for now we can simply discard the lower bit. We could perform some dithering, but some cores already dither to 6 bits and double-dithering is likely to lead to poor results – so we’ll avoid that for now. In the longer term, making the output bit depth of the guest core configurable is possible, but of course will require modifying the guest.

Next up is SDRAM:

   inout [ 16-1:0]  SDRAM_DQ, // SDRAM Data bus 16 Bits                                                                                                        
output [ 13-1:0] SDRAM_A, // SDRAM Address bus 13 Bits
output SDRAM_DQML, // SDRAM Low-byte Data Mask
output SDRAM_DQMH, // SDRAM High-byte Data Mask
output SDRAM_nWE, // SDRAM Write Enable
output SDRAM_nCAS, // SDRAM Column Address Strobe
output SDRAM_nRAS, // SDRAM Row Address Strobe
output SDRAM_nCS, // SDRAM Chip Select
output [ 2-1:0] SDRAM_BA, // SDRAM Bank Address
output SDRAM_CLK, // SDRAM Clock
output SDRAM_CKE, // SDRAM Clock Enable

The MiST’s SDRAM is 16 bits wide and provides 32 megabytes in a 13 row bits / 9 column bits layout – and luckily enough both iterations of the Turbo Chameleon 64 have chips with the same layout. The DE10-lite has a 64 megabyte chip in a 13 / 10 layout but this will still be compatible unless a core’s SDRAM controller happens to do full-page bursts. On the TC64 the chip select and clock enable pins are hardwired to save IOs, so we can simply leave those two signals unconnected.

Next is audio:

// audio
  output AUDIO_L,
  output AUDIO_R,

We have a single-bit output to each audio channel, typically fed from a sigma-delta converter or a PWM. If our target board had an external audio codec on board we’d have to choose between making invasive changes to the guest core, or feeding the audio bitstream into a filter to recover a multi-bit signal suitable for the DAC. Luckily, we don’t have to do either because the TC64 uses the same kind of single-bit audio outputs.

Now we have the most important signals for this project – SPI:

// SPI
inout SPI_DO,
input SPI_DI,
input SPI_SCK,
input SPI_SS2, // data_io
input SPI_SS3, // OSD
input SPI_SS4, // unused in this core
input CONF_DATA0, // SPI_SS for user_i

The broad design and function of these signals is inherited from the original Minimig project, on which a PIC microcontroller, the FPGA and the SD Card shared an SPI bus. Multiple streams of SPI data are shared over this bus, with the SPI_SS[2-4] and CONF_DATA0 signals acting as subsystem selects. At first glance this looks simple enough – we have data in, data out, clock and selects – but notice that SPI_DO is bidirectional. That’s going to cause us some difficulty because only signals which map directly to toplevel pins can be bidirectional in an FPGA design. If we’re going to replace the functionality of the MiST’s MCU within the FPGA we need to connect a new control module to these SPI signals – which makes them very much internal.

The good news is that the bidirectional mode is only used in one specific circumstance – ROM loading can be sped up by allowing the controller to drive the SPI clock, but the SD card to drive the data line – we thus have a strange ménage à trois going on bettween the controller, FPGA and SD card – some cores don’t use it at all, including the NES core, which makes it a good starting point. The bad news is that, for cores which *do* need it, we can’t leave the guest core completely unmodified – we will, unfortunately, have to replace this signal with separate inputs and outputs. We’ll worry about that later, though.

So let’s wrap up with:

// UART
input UART_RX,
input UART_TX
);

Pretty self-explanatory – and also irrelevant for the TC64, since it doesn’t have an RS232 serial port. (It is possible, however, to pipe RS232 data over the IEC port, and I’ve done this for debugging, and also for connecting an ESP8266 WiFi module.)

Next time I’ll look at the control module, and the services the guest core will need.

Leave a Reply

Your email address will not be published. Required fields are marked *