Verilog design flow

Source files:

For this hello world example we are going to use three Verilog files:

  1. We start with an edge-detector (edgeDetector.v):

module edgeDetector 
  ( input wire clock,
               reset,
               enable,
               inputValue,
    output wire risingEdge,
                fallingEdge);

  reg [2:0] edgeRegisters;
  
  assign risingEdge  = ~edgeRegisters[2] & edgeRegisters[1];
  assign fallingEdge = edgeRegisters[2] & ~edgeRegisters[1];
  
  always @(posedge clock)
    begin
      edgeRegisters[0]   <= (reset == 1'b1 || enable == 1'b1) 
                            ? inputValue 
                            : edgeRegisters[0];
      edgeRegisters[2:1] <= (reset == 1'b1)
                            ? {inputValue, inputValue}
                            : edgeRegisters[1:0];
    end

endmodule

This module will take the value of the inputValue when the enable input is active. This is done to make it possible to debounce the button used. It will detect the pressing on the button by the output risingEdge, and the releasing of the button by the output fallingEdge.

  1. The next module is a generic clock divider (genClockDiv.v):

module genClockDiv
  #(parameter integer clockFrequency = 50000000,
                      tickFrequency = 1000)
  (input wire  clock,
               reset,
   output wire tick);

  localparam reloadValue = clockFrequency / tickFrequency;
  localparam nrOfBits = $clog2(reloadValue+1);
  
  reg [nrOfBits-1:0] counterRegister;
  
  assign tick = (counterRegister == {nrOfBits{1'b0}}) ? 1'b1 : 1'b0;
  
  always @(posedge clock)
    counterRegister <= (counterRegister == {nrOfBits{1'b0}} || reset == 1'b1)
                       ? reloadValue - 1
                       : counterRegister - 1;

endmodule

This module will create tickFrequency number of ticks on the output tick given the clock frequency clockFrequency in Hz. We use this to debounce the button.

  1. The final module is the top-level (helloWorld.v):

module helloWorld
  (input wire        clock,
                     nReset,
                     nButton,
   output wire [3:0] colSelect,
   output wire [9:0] nRedLeds,
                     nGreenLeds,
                     nBlueLeds);

  wire s_reset = ~nReset; /* note: the user reset button
                                   is active low*/
  wire s_button = ~nButton; /* note: the buttons
                                     are active low*/

  assign colSelect = 4'd0; // we use column 0

  // first we instantiate the debounce counter
  wire s_dividedClockTick;
  
  genClockDiv
     #(.clockFrequency(74250000), // we use the 74.25MHz clock
       .tickFrequency(1000)) divider1 // we use a scanning of 1 kHz
     (.clock(clock),
      .reset(s_reset),
      .tick(s_dividedClockTick));
  
  // here is the edgedetection placed
  wire s_buttonPressed;
  
  edgeDetector isPressed
     (.clock(clock),
      .reset(s_reset),
      .enable(s_dividedClockTick),
      .inputValue(s_button),
      .risingEdge(s_buttonPressed),
      .fallingEdge());

  // we we do the color selection
  reg [2:0] colorSelectReg;
  
  always @(posedge clock)
    colorSelectReg <= (s_reset == 1'b1)
                      ? 3'b111 // after reset we show white
                      : (s_buttonPressed == 1'b1)
                      ? colorSelectReg - 3'd1 
                      : colorSelectReg;
  
  // finally we assign the Leds, note they are active low
  genvar n;
  
  generate
    for ( n = 0 ; n < 10 ; n = n + 1 )
      begin : genleds
        assign nRedLeds[n]   = ~colorSelectReg[0];
        assign nGreenLeds[n] = ~colorSelectReg[1];
        assign nBlueLeds[n]  = ~colorSelectReg[2];
      end
  endgenerate
  
endmodule

You can download these three Verilog files here.

Now that we have our design, we can create the files required for the CAD-tools.

Note

This page assumes an automated design flow, hence we will execute everything in a terminal.

Synthesis:

We are going to use yosys as synthesis tool. We can use for yosys a script file (yosys.script) that describes the source files, and which operation it should be performed:

read -sv edgeDetector.v
read -sv genClockDiv.v
read -sv helloWorld.v
synth_ecp5 -top helloWorld -json helloWorld.json

The first three lines indicate to yosys the design files in system Verilog (-sv). The last line instructs yosys to perform a synthesis for the ECP5-family of FPGA’s, that the top level module name is helloWorld (-top helloWorld), and that it should create a json file with the results (-json helloWorld.json).

Once this file is created a synthesis can be performed by:

yosys -s yosys.script

After execution you will have a file called helloWorld.json. This file is required for the next step.

Place and Route:

For place and route we are going to use nextpnr-ecp5. For the place and route tool to have information on which pins of the FPGA the ports are mapped, we require an lpf-file (gecko5.lpf):

BLOCK RESETPATHS;
BLOCK ASYNCPATHS;
SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 SLAVE_SPI_PORT=DISABLE MASTER_SPI_PORT=ENABLE SLAVE_PARALLEL_PORT=DISABLE;

LOCATE COMP "clock" SITE "G2";
IOBUF  PORT "clock" PULLMODE=NONE IO_TYPE=LVCMOS33;
FREQUENCY PORT "clock" 74.25 MHZ;

LOCATE COMP "nReset" SITE "G1";
IOBUF  PORT "nReset" PULLMODE=NONE IO_TYPE=LVCMOS33;

LOCATE COMP "nButton" SITE "M18";
IOBUF PORT "nButton" PULLMODE=UP IO_TYPE=LVCMOS18;

LOCATE COMP "colSelect[0]" SITE "B6";
LOCATE COMP "colSelect[1]" SITE "J3";
LOCATE COMP "colSelect[2]" SITE "K3";
LOCATE COMP "colSelect[3]" SITE "B17";
LOCATE COMP "nRedLeds[0]" SITE "A7";
LOCATE COMP "nRedLeds[1]" SITE "C6";
LOCATE COMP "nRedLeds[2]" SITE "D13";
LOCATE COMP "nRedLeds[3]" SITE "B12";
LOCATE COMP "nRedLeds[4]" SITE "C11";
LOCATE COMP "nRedLeds[5]" SITE "C10";
LOCATE COMP "nRedLeds[6]" SITE "E9";
LOCATE COMP "nRedLeds[7]" SITE "B9";
LOCATE COMP "nRedLeds[8]" SITE "C8";
LOCATE COMP "nRedLeds[9]" SITE "E7";
LOCATE COMP "nGreenLeds[0]" SITE "C7";
LOCATE COMP "nGreenLeds[1]" SITE "D6";
LOCATE COMP "nGreenLeds[2]" SITE "E13";
LOCATE COMP "nGreenLeds[3]" SITE "C12";
LOCATE COMP "nGreenLeds[4]" SITE "E11";
LOCATE COMP "nGreenLeds[5]" SITE "A11";
LOCATE COMP "nGreenLeds[6]" SITE "A10";
LOCATE COMP "nGreenLeds[7]" SITE "A9";
LOCATE COMP "nGreenLeds[8]" SITE "D8";
LOCATE COMP "nGreenLeds[9]" SITE "B8";
LOCATE COMP "nBlueLeds[0]" SITE "E6";
LOCATE COMP "nBlueLeds[1]" SITE "J4";
LOCATE COMP "nBlueLeds[2]" SITE "D12";
LOCATE COMP "nBlueLeds[3]" SITE "D11";
LOCATE COMP "nBlueLeds[4]" SITE "B11";
LOCATE COMP "nBlueLeds[5]" SITE "B10";
LOCATE COMP "nBlueLeds[6]" SITE "D9";
LOCATE COMP "nBlueLeds[7]" SITE "E8";
LOCATE COMP "nBlueLeds[8]" SITE "A8";
LOCATE COMP "nBlueLeds[9]" SITE "D7";
IOBUF PORT "colSelect[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "colSelect[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "colSelect[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "colSelect[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;

The first section of this file contains some generic assignments that you can copy directly. The next sections contain to definitions for the clock input (we use the 74.25MHz clock as described here), the nReset input (see also here), the nButton input (we use button 1 as described here), and finally the RGB-array (as described here).

Important

The names in this file are case-sensitive, hence they should equal the case as in your top-level design file!

Once this file is present, the place-and-route can be performed by:

nextpnr-ecp5 --threads 12 --timing-allow-fail --85k --package CABGA381 --json helloWorld.json --lpf gecko5.lpf --textcfg helloWorld.config

The parameters given represent:

  • –threads 12: the maximum number of threads used.

  • –timing-allow-fail: continue even if the timing is not met.

  • –85k: the size of the FPGA mounted on the board.

  • –package CABGA381: the package of the FPGA mounted on the board.

  • –json helloWorld.json: the synthesized design by yosys.

  • –lpf gecko5.lpf: the pin definition file.

  • –textcfg helloWorld.config: the output file that is going to be used to generate the bit-file.

Bit-file generation:

For the bit-file (that we can use to configure the FPGA) generation, ecppack is used. To generate the bit file:

ecppack --compress --freq 62.0 --input helloWorld.config --bit helloWorld.bit

The parameters given represent:

  • –compress: compress the bit-file such that is takes lesser space.

  • –freq 62.0: The download frequency when reading from external SPI-flash.

  • –input helloWorld.config: the place-and-routed design.

  • –bit helloWorld.bit: The bit-file that we can use to configure the FPGA.

Configuring the FPGA:

To configure your GECKO5Education/Modular with your design, the tool openFPGALoader is used. To configure your FPGA, use:

openFPGALoader helloWorld.bit

You should now see that the first column of LED’s will light up white, and each time you press the button their colors will change.

Note

You can also put your design into the flash on the board by using:

openFPGALoader -f helloWorld.bit

However, this will wear off your flash, so only use it, for example, if you want to prepare yourself for a project presentation where you do not want to download the design by hand.

Complete automation:

To not have to type in the commands you can also create a command-file (synthesize.sh):

yosys -s yosys.script
nextpnr-ecp5 --threads 12 --timing-allow-fail --85k --package CABGA381 --json helloWorld.json --lpf gecko5.lpf --textcfg helloWorld.config
ecppack --compress --freq 62.0 --input helloWorld.config --bit helloWorld.bit
openFPGALoader helloWorld.bit

and make it executable by:

chmod +x synthesize.sh

All the files can be found here.