Generalising the Core

Porting a Core, DeMiSTified – Part 4 – 2021-03-03

(Before I start this installment, I should say that I have now released the Chameleon port of the NES core – I’m not going to delay the core just to talk about the process!)

So we have a project file for the MiST version of the core, and we need to create project files for other platforms. My scripts and Makefiles will produce these files, so what I need to do is extract a list of components from the .qsf project file for MiST.

Opening this file in a text editor reveals a list of files at the end which look like this:

set_global_assignment -name QIP_FILE "mist/mist-modules/mist.qip"
set_global_assignment -name SYSTEMVERILOG_FILE mist/NES_mist.sv
...

I’m going to take these lines and place them in a .qip file, which is also a text file (in fact it’s a TCL script – see the previous installment!) containing lines in a very similar format:

set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) mist/mist-modules/mist.qip ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) mist/NES_mist.sv ]
...

The difference is merely the joining of the file path and filenames, which allows a group of files to be added as a single component no matter where they are in the filesystem. This gives us a single file we can add to our various board-specific projects. I’m going to place this in the topmost directory, which is where the MiST project files live. I could place it anywhere else if I wanted to, but I would have to adjust the paths to the other files, accordingly. For instance, if I placed it in the src directory, I would have to remove any references to “src/” from the filenames, and add “../” to any files which are in the mist directory instead.

There is one file from the MiST project file that I’m going to omit – namely clk.v. The reason is that this is a board-specific PLL definition which generates the clocks needed by the core from the board’s incoming 27MHz clock. None of my target boards has a 27MHz clock, so I need to replace this component with an equivalent for each of my targets.

I have checked that the core doesn’t use the incoming 27MHz clock for anything other than generating further clocks, so it doesn’t actually matter what speed this clock is; all that matters is that the PLL correctly translates from the incoming clock speed, whatever it is, to the clock speeds required by the core. Therefore, I’m going to feed each board’s incoming base clock into the MiST toplevel’s CLOCK_27 signal, regardless of its actual speed, and then provide a board-specific replacement for the clk.v component.

These will live in a plls directory, which will contain a subdirectory for each board and within that, a board-specific implementation of clk.v which I will create using the megafunction wizard. So I just need to know what clock frequencies I require. The easy way to find this out is to build the MiST core, then in the hierarchy view double-click the PLL to open the megafunction wizard and read the values specified. Or I can just read the pll.v file, and look for these lines:

altpll_component.clk0_divide_by = 11,
altpll_component.clk0_duty_cycle = 50,
altpll_component.clk0_multiply_by = 35,
altpll_component.clk0_phase_shift = "0",
altpll_component.clk1_divide_by = 44,
altpll_component.clk1_duty_cycle = 50,
altpll_component.clk1_multiply_by = 35,
altpll_component.clk1_phase_shift = "0",

We know the incoming clock is 27MHz. Both generated clocks multiply this by 35, so 945 – then one clock divides that result by 11, and other divides it by 44, so that gives me one clock of just under 86MHz and the other a quarter of that, so just under 21.5.

I use the megafunction wizard to create these two clocks from a base clock of 8MHz for Chameleon64 v1, and 50MHz for both Chameleon64 v2 and DE10-lite. The name of these generated IP blocks must match what the guest core’s expecting, so in all cases I merely call it clk. plls/${board}/clk.qip is then added to each board’s project file by the build scripts.

There is one other minor complication to take care of: one of the memories within the design has an initialisiation file, nsf.hex. By default Quartus likes to place these alongside the project file, but since I’m going to have multiple project files in different directories, this is inconvenient. Quartus will find the .hex file if its in the same directory as the memory file that references it – which in this case is mist/NES_mist.sv – so I’m going to move the .hex file to the mist directory. With that done, we’re ready to add the compatibility layer needed to make the core run…

Leave a Reply

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