Part 2: Specification – RTL Design Success

Author: Craig Maiman, Principal RTL Design Consultant

Welcome to the RTL Design Success series — Part 2 of 9

1.    Specification

Once you have settled on a micro-architecture then it is time to write the specification describing it.  I have always put a lot of effort into the specification to make sure it is clear, complete, and unambiguous.  That effort up-front makes for easier work down the line.

It should be complete enough that any software person that must write code to interface to the hardware could do so without asking the designer any questions.  Also, it should be complete enough that if another person were writing the RTL to implement it, they should similarly be able to do it without assistance.

The following are some examples of what should be clearly addressed by the micro-architecture specification:

  • Requirements/goals
  • Micro-architecture overview
  • Top-level functionality in detail
  • Sub-block functionality
  • Software API (e.g., memory maps, registers, functionality, reset state, etc.)
  • Interfaces (signals, protocols, etc.)
  • Resets (hard and soft)
  • Clocks
  • Asynchronous interfaces
  • Test modes
  • Performance, power, area
  • Target device and cost estimates if applicable
  • Verification requirements (e.g., functionality to test, including corner cases)
  • References & Standards

2.    Abstractions

Using higher-level abstractions in your coding can be pay many dividends in your development cycle including coding efficiency, readability, debug, ease of code changes and maintenance down the road.  While it takes time up-front to set up coding abstractions, it more than pays off once that’s done.

The simplest abstraction would be the commonly used enumerated type.  While enumerated types is typically used in state machines, that’s certainly not the only place it can be used.  It can also be used for any bus you expect to carry control-oriented non-arithmetic information such as a request, acknowledgment, response, status, etc.

An example of a state machine enumerated type would be as follows:

Enumerated types are used by most designers, so we won’t spend any more time on them in this paper.

A more interesting abstraction in SystemVerilog is Structs, which many designers will be familiar with from any C coding they have done.

A simple example of a Struct definition, which you would put a SystemVerilog Package file (see here on how to define a package and import it into logic modules), would be as follows:

This Struct is just a collection of simple logic signals and can be used for communication between logic within a Module or used in interfaces between Modules.  Now, instead of having to list all those signals in the interface list of multiple Modules and up and down a hierarchy, you just specify a “signal” of type opb_buf_req_data_type.  If you need to make changes to this interface, you only need to modify the Package definition and not all the interface definitions (of course, you may still need to modify the source and destination logic).

The various parts of the declaration are as follows:

Typedef: Defines a type so you can declare variables of that type

Struct: A structure consisting of components of various types.

Packed: The default is “unpacked” which means the simulator can store components of the Struct however it sees fit.  To force the Struct to be contiguous bits, you must specify packed.  This also allows for the designer to be able to calculate the size of the struct in bits.  For example, if you need to size FIFOs or memories that the Struct might be wired to.

The bits will be stored as follows: The first listed component of the Struct will be the high-order bits and the last component will be the low-order bits (though, usually this doesn’t matter to the designer).

An example of a more complex Struct would be as follows:

This Struct example not only has logic signals and enumerated types, but it also has another Struct!  It has:

  • Valid bit – buf_valid, a single bit logic signal
  • Request – buf_req, an enumerated type indicating the type of request (e.g., Read, Add, Remove, etc.)
  • Source – buf_src, an enumerated type indicating the source of the request (e.g., Transmit, Receive, etc.)
  • Address – buf_addr, a logic type bus
  • Data – buf_data, a Struct (shown previously), which contains various information used in the Request.

After I initially defined this Struct and was testing the logic in simulation, I found bugs which required adding various information in the data portion of the Struct.  All I had to do was make the changes in the package file and the source and destination logic.  No changes where required in any of the interface definitions.

The interface was defined as follows:

So much simpler than if I had had to spell out all the signals embedded in the Structs.  Also consider using a “Interface” type if you have multiple interfaces with the same interface.

It is also easy to use the Structs in your logic.  Here’s an example of assigning signals to Struct fields (this snippet just shows one case value, not the whole case statement):

This is a complicated example because the source and destination in this case are of different Struct definitions.  While they have many similar fields, there are differences, so it required a different Struct type for each.  In this case you must do the assigns field-by-field.  If the two (opb_bug_wdata and opb_buf_req_fifo_q2) were of the same Struct type, then the assignment could simply be opb_bug_wdata <= opb_buf_req_fifo_q2 and the whole structure is assigned!

Another thing to note about the above case statement is the “unique” case statement.  Unique indicates that the values listed below (only one shown here) are mutually exclusive (two or more will never be true at the same time).  If you don’t indicate that they are “unique” it will be interpreted as priority, which will result in more complex logic.  Just make sure you have all the possible values!  If not, it could result in indeterminate outputs (put a default value if necessary to cover all values not specifically listed).  See more about this here.

An example showing how to test fields of a Struct:

In this example we’re testing a field of a Struct by checking if it is equal to a value of an enumerated type (which of course matches the field definition type used in the Struct definition).

Here is a 3rd example of using Structs in logic:

In this example we’re testing various fields of a Struct in a case statement to determine the next state to go to in a state machine.  It some cases we are testing against an enumerated type value and for other fields just comparing against constants.  You can see how readable this becomes when using all the abstractions.

Using Structs like the above examples shows great dividends in simulation too.  Many simulation wave viewers will show all the field values if you hover the cursor over a Struct-defined signal.  The “signal” will be one line of wave in the view, but hovering will produce a dropdown from the cursor showing all the field values.  Much more compact and readable than having all the signals taking up many lines of the wave view. 

Here’s an example:

XtremeEDA is an experienced partner you can trust!!

Cadence Design Systems helps engineers pick up the development tempo. A leader in the market for electronic design automation (EDA) software, Cadence sells and leases software and hardware products used to design integrated circuits (ICs), printed circuit boards (PCBs), and other electronic systems. Semiconductor and electronics systems manufacturers use its products to build components for wireless devices, networking equipment, and other applications. The company also provides maintenance and support, and offers design and methodology consulting services. Customers have included Pegatron, Silicon Labs, and Texas Instruments. Cadence gets more than half of its sales from customers outside the US.

Synopsys, Inc. (Nasdaq:SNPS) provides products and services that accelerate innovation in the global electronics market. As a leader in electronic design automation (EDA) and semiconductor intellectual property (IP), Synopsys’ comprehensive, integrated portfolio of system-level, IP, implementation, verification, manufacturing, optical and field-programmable gate array (FPGA) solutions help address the key challenges designers face such as power and yield management, system-to-silicon verification and time-to-results. These technology-leading solutions help give Synopsys customers a competitive edge in quickly bringing the best products to market while reducing costs and schedule risk. For more than 25 years, Synopsys has been at the heart of accelerating electronics innovation with engineers around the world having used Synopsys technology to successfully design and create billions of chips and systems. The company is headquartered in Mountain View, California, and has approximately 90 offices located throughout North America, Europe, Japan, Asia and India.

asicNorth was established in January 2000 with one purpose in mind: deliver the highest quality design services possible. In an industry that can be quite volatile at times, it is important to have a design partner that you can depend upon to deliver the skills you need when you need them. A project can only be successful if there are:

Top quality skills on the team
Communication with the customer
Attention to detail
Cost sensitivity
Focus on the schedule

Today, asicNorth is enabling high-tech industry leaders and startups alike with a combination of digital, analog, and mixed-signal design capabilities. Driven to produce successful results, asicNorth is Making Chips Happen™.

Codasip delivers leading-edge RISC-V processor IP and high-level processor design tools, providing IC designers with all the advantages of the RISC-V open ISA, along with the unique ability to customize the processor IP. As a founding member of RISC-V International and a long-term supplier of LLVM and GNU-based processor solutions, Codasip is committed to open standards for embedded and application processors. Formed in 2014 and headquartered in Munich, Germany, Codasip currently has R&D centers in Europe and sales representatives worldwide. For more information about our products and services, visit For more information about RISC-V, visit

Founded in 1999, Avery Design Systems, Inc. enables system and SOC design teams to achieve dramatic functional verification productivity improvements through the use of

Formal analysis applications for RTL and gate-level X verification;

Robust Verification IP for PCI Express, USB, AMBA, UFS, MIPI, DDR/LPDDR, HBM, HMC, ONFI/Toggle, NVM Express, SCSI Express, SATA Express, eMMC, SD/SDIO, Unipro, CSI/DSI, Soundwire, and CAN FD standards.

Siemens EDA
The pace of innovation in electronics is constantly accelerating. To enable our customers to deliver life-changing innovations to the world faster and to become market leaders, we are committed to delivering the world’s most comprehensive portfolio of electronic design automation (EDA) software, hardware, and services.