Search:

PmWiki

pmwiki.org

edit SideBar

Main / Hdl

Back to FPGAs

Design Tips

  • avoid async resets on BRAMs and DSPs
  • limit low fanout control signals (clk, clk_en, set/reset)
  • avoid global resets
  • fewer control sets increases placement flexibility
  • avoid use of both set+reset on one FF
  • avoid active-low control signals since they require inversion to active-high often

Handy tool for converting VHDL to Verilog here: https://github.com/ldoolitt/vhd2vl

VHDL Notes

VHDL is NOT case sensitive.

VHDL files often start with library inclusions like:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

library unisim;
use unisim.vcomponents.all;

These IEEE libraries include definitions for std_logic and std_logic_vectors, which are arrays of bits. A whole bunch of functions are described that can be used with these data types, like and, nand, or, xor, etc.

You can define a single bit std_logic_vector: std_logic_vector(0 to 0)

There are two ways to put sub-blocks into your architecture. You can either put a component declaration at the top of your arch and then instantiate it in the body, or you can instantiate an entity in the body that is in the work library using entity : work.foo.

Since there are no macros per se in VHDL, for conditional inclusion of blocks of logic use generics and generates. A generic is included in the entity above the ports:

  generic (name : type);
  port(...);

Optionally, a constant can be assigned by adding a := value after the type.

Generate statements

Then, conditionally build some logic with a generate:

  foobar_gen : if DEBUG or FOO_TYPE = "superfoo" generate
    foobar_proc : process
    begin
      wait until (foo_clk = '1');
      if (foo_rst = '1') then
        ...
      end if;
    end process foobar_proc;
  end generate foobar_gen;

It appears to nest a conditional, you must separate into two clauses. Also, note that in compile the validity of the inner statements are checked regardless of the outer statement and this can result in warnings.

foo_gen : if foo_g = "yayfoo" generate
      bar_gen : for i in 0 to 2 generate

In other words, the VHDL generate statement does not accomplish "conditional compilation". The contained statements are always compiled, but if the conditional is false, they are not elaborated (in SW terms, they are not "linked"). Any statements must be legal VHDL, and all referenced objects must exist. So you can't get away with invalid code inside a generate statement even if the conditions aren't met to generate it.

Conditional use of configurations

There doesn't seem to be a clean way to do this (i.e. no 'else' for generates, etc) so we must instantiate in code a component for each potential component. Of course it only gets created if the conditional passes during sim/synth. Here's an example of this, also throwing in some complication in that there are many of these foo guys sharing the foo_wrapper and they need an index into specific bit positions of a shared vector. Hence the for loop.

foo_gen : for i in 1 to 1 generate
   begin
   rock_gen : if music_g = "rock" generate  
   for rock : foo_wrapper USE CONFIGURATION work.rock_c;       
   begin rock : foo_wrapper
      generic map...
      ...
   end generate rock_gen;

   roll_gen : if music_g /= "roll" generate
   for roll : foo_wrapper USE CONFIGURATION work.roll_c;
   begin roll : foo_wrapper
      generic map...
      ...
   end generate roll_gen;
end generate foo_gen;

You cannot concatenate bits together in a port map value assignment, but you can separate the port name as needed. To assign your array ports to all zeroes, use ( (others => '0'), (others => '0') ) or (others => ( others => '0') )

Verilog

Learning Sites:
https://www.cis.upenn.edu/~milom/cis371-Spring11/lab/textbook-verilog-tutorial/VOL/main.htm
http://www.asic-world.com/verilog/intro.html

Header files

Verilog header files have the extension .vh and they are included with

`include "filename.vh"

Here is a template for a vh file with an inclusion protection and illustrating the way to define macros. You can also build functions in these.

`ifndef _FILENAME_VH_
`define _FILENAME_VH_

`define FOO    42

`endif

Signs

In Verilog a reg contains binary data, signed vs unsigned is just a matter of interpretation. The bit values stay the same, subtraction and addition are always performed using two's complement.

Assigning all ones to a bus of variable width

wire foo;
assign foo = {32{1'b1}};
assign foo = ~0;

Either way should work.

Abtraction Levels

Behavioral level (highest)
This level describes a system by concurrent algorithms (Behavioral). Each algorithm itself is sequential, that means it consists of a set of instructions that are executed one after the other. Functions, Tasks and Always blocks are the main elements. There is no regard to the structural realization of the design.

Register-Transfer Level
Designs using the Register-Transfer Level specify the characteristics of a circuit by operations and the transfer of data between the registers. An explicit clock is used. RTL design contains exact timing bounds: operations are scheduled to occur at certain times. Modern RTL code definition is "Any code that is synthesizable is called RTL code".

Gate Level (lowest)
Within the logic level the characteristics of a system are described by logical links and their timing properties. All signals are discrete signals. They can only have definite logical values (`0', `1', `X', `Z`). The usable operations are predefined logic primitives (AND, OR, NOT etc gates). Using gate level modeling might not be a good idea for any level of logic design. Gate level code is generated by tools like synthesis tools and this netlist is used for gate level simulation and for backend.

Does using <signal_name>'RIGHT mean the last bit on the right of the signal?

Conditional Processes

You can actually put always blocks inside of if statements, which presumably gates them and they are only active under certain conditions (more research needed).

Edges

posedge and negedge are functions. If one signal in the sensitivity list uses them, then all signals must.

Parameterized Modules

You can create a module "template" that will let you set up and use variable values for things like signal widths, whenever you instantiate said module.

module foobar #(PARAM1=42, PARAM2=42)
(
  //blah blah
);
endmodule

Syntax Tips

Creating a module template. Ports can be in any order, and is a list of names only, with direction defined inside definition body.

module <module_type> ( <portlist> );
  : type name(ports);
end module

always @(*) begin asks the compiler to build the sensitivity list for you based on what's inside the block.

= is used for blocking assignments, while <= is used for non-blocking assignments. This helps in generating combinational or sequential logic. The blocking assignment blocks the next assignment from occurring until the current one is complete. It's counter-intuitive, because the blocking assignments happen faster given that they are not waiting on anything except wire time. Therefore blocking assignments makes sense for combinational logic, but non-blocking assignments make sense for sequential logic.

reg vs wire
A signal assigned to a submodule output cannot be a reg, it must be declared as a wire.

Crossing Clock Domains

   //--------------------------local clock domain------------------
   // Verilog Example: 
   // crossing transaction ACK from remote state machine to local state machine
   // 
   always @(posedge local_clk)
   begin
      if (reset)
      begin
        remote_ack_local_reg1 <= 1'b0 ;
        remote_ack_local_reg2 <= 1'b0 ;
        remote_ack_local      <= 1'b0 ;
      end
      else
      begin
        remote_ack_local_reg1  <= remote_ack ;
        remote_ack_local_reg2  <= remote_ack_local_reg1 ;
        remote_ack_local       <= !remote_ack_local_reg2 && remote_ack_local_reg1 ;
      end
   end

How about another? In this case we are interested in the level, and when the input signal de-asserts.

   //--------------------------remote clock domain------------------
   // Verilog Example: crossing the REQ from local state machine to the
   // remote state machine
   //
   always @(posedge remote_clk)
   begin
      if (reset_rem)
      begin
        local_req_remote_reg1     <= 1'b0 ;
        local_req_remote_reg2     <= 1'b0 ;
        local_req_remote_start    <= 1'b0 ;
        local_req_remote_end      <= 1'b0 ;        
      end
      else
      begin
        local_req_remote_reg1     <= local_req_remote ;
        local_req_remote_reg2     <= local_req_remote_reg1 ;
        local_req_remote_start    <= !local_req_remote_reg2 && local_req_remote_reg1 ;
        local_req_remote_end      <= !local_req_remote_reg1 && local_req_remote_reg2 ;        
      end
   end

Parameters Vs Macros

The tick ` is used for macros, not parameters. So when you use something like `define VAR, then you reference `VAR but with parameter WIDTH = 5 then you just reference WIDTH in the code.

Functions and Tasks

Tasks can include time delays, but functions cannot as they are combinational only. So functions cannot call tasks, but can call other functions. Tasks can have multiple outputs, but functions have only one output. Variables declared within are local, but can modify global variables. Oddly, a function is required to have at least one input.

A function name is also assigned within the function as the return value. For example:

function integer log2;
   input [31:0] value;
   for (log2 = 0; value > 0; log2 = log2 + 1)
       value = value >> 1;
endfunction

Here's an example function to generate a large field of data for your testbench:

function [DATA_BITS-1:0] count_fill;
    input useless;    
    integer i;
    integer start_data;
begin    
    start_data = 32'h01010101;
    count_fill = 0;
    for ( i=0; i < 128; i = i+1 ) begin
        count_fill = (count_fill << BITS ) | start_data;
        start_data = start_data+ 32'h01010101;        
    end     
end
endfunction

Verilog Simulation

Timing

The `timescale directive is NOT synthesizable. It is to tell the simulation how time is defined (i.e. what does #1 mean?) and is specified with <time_unit>/<time_precison> as in `timescale 1ns/1ns'. The latter indicates how delay values are rounded in simulation. The time units available are s/ms/us/ns/ps/fs.

While

A while statement executes the code within it repeatedly if the condition it is assigned to check returns true. While loops are not normally used for synthesizable models, but they are used in test benches.

Initial

An initial block, as the name suggests, is executed only once when simulation starts. This is useful in writing test benches. If we have multiple initial blocks, then all of them are executed at the beginning of simulation.

initial begin
  clk = 0;
  reset = 0;
  req_0 = 0;
  req_1 = 0;
end

Note that the commands inside execute sequentially, so delays are compounding even when applied to different signals. In this example, the enable happens 60 time units after start, not 20.

initial begin
    enable = 1'b0;
    reset = 1'b0;
    #20 reset = 1'b1;
    #20 reset = 1'b0;
    #20 enable = 1'b1;
end

SystemVerilog

Net variables are static type, which is global scope and fixed size throughout simulation. Dynamic types are Class, Queue, Associative Array and do not have a fixed size but vary during run.

Note that Verilog does not have an enum, but SV does. However, SV is not allowed for a top level RTL module by Vivado.

In SV you are allowed to put function definitions at global scope, but this is not allowed for Verilog. If you try Vivado will give you this error: root scope declaration is not allowed in verilog 95/2K mode.

Note that Verilog does not have the import instruction, while SV does. If you don't have the right file type assigned, Vivado will only give you a stupidly useless "syntax error" failure note instead of pointing out the real problem.

Declarations

Some online examples show declarations of ints inside an initial begin block, but the Vivado 2017 compiler won't actually let you do this. Must be outside the block.

Glossary

  • asynchronous circuit = sequential digital logic which is not driven by a clock; problem is race conditions on variable delays
  • combinational logic = output depends purely on current inputs; no memory and no delay
  • combinatorial logic = same as combinational logic
  • flip-flop = a latch controlled by clock transition (or sometimes used interchangeably with latch)
  • latch = basic sequential logic memory/storage element driven by input levels and not transitions
  • sequential logic = output may depend on previous input as well as current input; has memory; may depend on a clock signal to drive a latch

Verilog-specific

  • always_comb (SV) = block models a combinational circuit, sensitivity to all right-side expressions, no memory (shouldn't infer a latch in synthesis)
  • always_ff (SV) = block models a register (should infer FF or register in synthesis)
  • always_latch (SV) = block models a latch

import

An import function let's you bring in stuff from other "packages". Here's the syntax:

import axi_vip_pkg::*

Mixed-Language Gotchas

When instantiating VHDL in Verilog, only the following VHDL data types are supported: bit, bit_vector, std_logic, std_ulogic, std_logic_vector, std_ulogic_vector. See chapter 9 of UG901 for details of Vivado mixed language support.

Cannot use the VHDL integer data type for simulation.


Page last modified on June 01, 2023, at 04:47 PM