Newsletter 2020 Q3 Detail

Testing Volatile Registers

Introduction:

A register in an IP block can typically be accessed from two interfaces, one from the cpu/software side i.e. the standard bus protocol like AMBA-AHB, AMBA-AXI, etc., and the other from the hardware side which has some signals that are connected with the application logic or some sensor interface.

Software Interface:

There are approximately 32 register software accesses which include some special accesses. These include rw, ro, r/w1c(read/write one to clear), rs(read to set), etc.

Hardware Interface:

The hardware interface can only have rw, ro, and wo types of access or there may not be any access from the hardware side at all.

If registers can be written from the hardware side they are known as volatile registers. These can not only be accessed by software interface but can also be written from the hardware interface.

So, both sides, software and hardware, can read/write the register memory simultaneously. The challenge is to test these types of registers.

Individual testing can be done easily using different environments like board prototyping or some UVM simulation environments. But to set up an environment which can test the registers simultaneously with both interfaces is a challenge for verification engineers because at any instance any one of the interfaces can read/write the register memory which can cause test failure.

Board prototyping is a complete C-based debugging environment in which an IP register can be tested with processor simulation.

On other hand, a UVM simulation environment is a UVM based environment which works on standard bus agent drivers.

A solution to test volatile registers is to combine a C-test environment (processor simulation) and a UVM simulation Environment.   

Bare-Metal Verification:

In this environment, C-tests are used to drive the standard SW bus through the processor transactions and, on the other hand, the hardware side is driven by the UVM agent. Along with these, an extra synchronizing block is used in parallel with the test block, i.e., IP1 and IP2 (containing volatile registers) which can help in synchronizing the software and hardware transactions on the registers. Not only the volatile registers but also special registers in an IP like Lock registers, shadow registers, indirect registers, etc. are supported.

6-1

Figure 1: Placement of “sync block” with other modules IPs in the environment

Some Rules for the Synchronization:

  • C program accesses this sync block through front-door (bus transaction) and UVM accesses this space using back-door.
  • The size of the block can be increased depending upon the number of registers to be tested.
  • The first five locations are used for synchronization and others can be used to pass the information from C to UVM or vice versa.
  • Below are the certain commands for synchronization.

2-pasted-image-0

How UVM/C-tests Work for Volatile Registers

  1. The test is initialized by loading the “sync block” using the command sync.test_start() or test_h->test[0].dw = 0x1. It can be done by the hardware/software side of the test block depending upon the access.
  2. In the test algorithm,
    1. The register is loaded with the value from the side where it is writable and the same value is loaded on the “sync block”.  
    2. For the same, the register is read from the other side and the read value is compared with the data at the sync block. 
    3. If the value mismatches, then a simulation error is generated.
  3. After that, the test is stopped with the  command “sync.test_stop()” from the hardware side or “test_h->test[0].dw = 0x0” from the software side.

Example: 

Sample tests for GPIO registers. 

  1. Below UVM code is used to derive the “gpio_in” register from the hardware side.

3-pasted-image-0

  • Below is the C-test to test “gpio_in” register from the software side.

4-pasted-image-0

Conclusion:

This synchronization method is efficient to test the volatile registers. Automating this technique is helpful in improving quality and reducing time in all aspects of verification.

Use of SystemVerilog Structures instead of Interface

Introduction

In SystemVerilog, hierarchical modules can be connected by simple data types, complex data types (structs, unions, etc.), or interfaces. The aggregating of all the signals between two modules in one place which simplifies maintenance of the code could be done using IDesignSpecTM.

Alternatively, this could have been done using interfaces. However, this could be easily done using structures. These structures are supported by many CAD tools, and they don’t need to be instantiated as in the case for interfaces.

Usage of structure could be hierarchical while interfaces do not allow such a feature. The nesting of structures is also allowed while interfaces are not allowed for all EDA tools.

A struct is used when all the signals within the struct follow the same port direction: input or output. It becomes challenging to use struct when driving directions are mixed.

IDesignSpecTM also supports the SystemVerilog structures instead of interfaces in SystemVerilog and generates structures at the addrmap level to bundle ports coming out of the generated RTL from IDesignSpec.

For every register, there will be two structs: one for hardware to Block (input) and another for Block to Hardware (output) inside the package. This is done to avoid confusion between input and output ports.

Structs are also supported for repeat on section/reg and external registers and blocks. In the case of external a separate package file is created which is further imported in the block package. If there exists a chip in the spec, then the chip package will be created and further imported in the chip wrapper.

In the case of struct the possible values are equal to struct, struct:bus_inf, struct:bus_struct.

Case 1: sv_interface=struct

In this case, all the bus signals will be flattened and the signals from Hardware to CSR and CSR to Hardware will be placed inside the struct in the package.

1

Figure 1: Bus signals flatten, HW Ports inside struct

Case 2: sv_interface=struct:bus_inf

In this case, all the bus signals are placed in the interface and the signals from Hardware to CSR and CSR to Hardware will be placed inside the struct in the package.

2

Figure 2: Bus signals inside interface, Hw ports inside struct

Case 3. sv_interface=struct:bus_struct

In this case, all the bus signals are also placed inside the struct in the package and the signals from Hardware to CSR and CSR to Hardware will be placed inside the struct in the package.

3

Figure 3:Bus Signals and Hw ports both are inside struct

Flow Chart of Structure:

4

Figure 4: Flow chart of Structure

 

IDS also supports a deep hierarchy of sections (reg-groups). A separate package file is created for the section, which is further imported in the block package file and then block package file is imported in top wrapper file.

RDL Example:

The example below shows the application of struct property on an addrmap in SystemRDL with the help of a UDP declaration :

property sv_interface {type = string; component = addrmap ; };

addrmap block_name {

desc = “GMC Level RDL Description”;

`include agnisys_ip1.rdl

sv_interface = “struct”;

section1 section1[2] @0x0400;

};

//// agnisys_ip1.rdl  ////

addrmap section1 {

name = “agnisys_ip1 Address Map”;

reg reg_name {

regwidth = 32;

field {} f1[2:0];

};

reg_name reg_name[6] @0x00;

};

Summary

SystemVerilog structs are handy constructs that can encapsulate data types and simplify your RTL code. They are most effective when the structure types can be used throughout a design, including as module ports, and with modules that support parameterized data types.

Reference

https://www.agnisys.com/release/docs/ids/InterfacesandstructuresinRTL.html

Parameterization of Widgets

IDesignSpecTM supports different bus protocols for high performance data transfer among the Intellectual Property (IP) cores. The protocols like AMBA-AHB, AMBA-AXI, AMBA-APB, AVALON, I2C, SPI, TileLink, OCP or proprietary buses differentiate on features such as pipelining and burst.

Each bus protocol has a widget. The widgets convert bus protocols into an internal bus (custom bus) for accessing registers. Some bus protocols have different flavors and versions based on functionalities. The following bus protocols are supported along with their corresponding widget files:

  • AMBA-APB:Three flavors of APB widget are supported: flopped APB (outputs are flopped), which is beneficial in case of timing analysis, combinatorial APB widget, which uses fewer flops, and pulse pready APB (to change the behavior of PREADY during PSEL assertion) in order to make it compatible with third party flow asynchronous bridging modules.
  • AMBA-AHB:Based on AMBA AHB bus protocol
  • AMBA3AHBLite:Based on AMBA 3 AHB Lite protocol
  • AXI4FULL:Two flavors of AXI4FULL widget are available: pipelined in which simultaneous read/write is supported and non-pipelined in which if a read and write transaction occurs together then it is broken into 2 transactions. First the write operation is completed and then read takes place.
  • AXI4Lite:Similarly, the AXI4Lite widget also has pipelined and non-pipelined versions.
  • AVALON:Two Avalon widgets are available, with burst transfer and with no burst.
  • I2C:Based on I2C protocol with 7-bit addressing
  • TileLink:Based on TileLink protocol 1.7 and 1.8 protocol
  • SPI:Based on SPI protocol
  • Proprietary:Custom bus protocol
  • WISHBONE:Based on Wishbone protocol having two flavors with ack_i as synchronous and asynchronous

 

Widget Property

 

The ‘widget’ property can be used to generate a widget file in the output according to the bus protocol used in the RTL.

This property can be applied either on the Chip or the Block component. According to the values specified as arguments in the property, the appropriate widget will be produced in the output.

User can specify the following arguments:

{widget=Bus_Name:Type:Module_Name} 

where,
Bus_Name: It can take the bus name for which the user wants to generate the

widget for its RTL.
Type: It will define the specific flavor of the widget used such as-

  1. ff : flopped(ff) for APB Bus only
  2. pulse_pready : For APB only
  3. np : non pipeline(np) for AXI or AXI-4FULL bus only
  4. no_burst: For Avalon bus only
  5. 1_7: for TileLink bus only
  6. wishbone_widget_async: For Wishbone bus only

Module_Name: It specifies the user defined name with which the widget will be

generated.

 

Note:
1) In case the user does not wish to specify any argument, it can be left blank, but specifying colon is mandatory in such case,

e.g., {widget=apb::chip_apb_widget} or  {widget=apb::}

The default value will be taken for the argument which is left blank such as the pipelined version of AXI and AXI-4FULL widget, 1.8 version for TileLink, and burst enabled for Avalon bus.

Below mentioned are all the possible values for the first and second argument which the user can specify in the widget property:

 

widget = apb::abp_widget

widget = apb:ff:apb_widget

widget = apb:pulse_pready:apb_widget

widget = axi:np:axi_widget

widget = axi:: axi_widget

 

widget = axi4full:np:axi4full_widget

widget = axi4full:: axi_widget

 

widget = tilelink:1_7:tilelink_widget

widget = tilelink:: tilelink_widget

 

widget = avalon:no_burst: avalon_widget

widget = avalon:: avalon_widget

 

widget = wishbone:wishbone_widget_async: wishbone_widget

widget = wishbone:: wishbone_widget

 

widget = spi::SPI_Slave

widget = i2c::I2C_Slave

widget = ocp::ocpip_widget

widget = amba:: amba_widget

widget = amba3ahblite:: AMBA3AHBLITE_widget

 

RDL Example:

 

The example below shows the application of widget property on an addrmap in SystemRDL with the help of a UDP declaration :

 

property widget {type=string;component=addrmap;};

addrmap Block1 {

widget = “apb::abp_widget”;

reg Register1 {

field {

hw = r;

sw = rw;

}Field1[31:24] = 8’h2;

};

Register1 Register1;

};

 

 

Output:

Enhancement has been done to combine different flavors of the modules into one and selection will be based on a parameter value. Therefore, only one file will be generated for each bus protocol.

 

Suppose the widget property is applied in the specification as shown below:

Widget=apb:ff:apb_widget. The following highlighted change will appear in the output Verilog file:

apb_widget #(.addr_width(addr_width), .bus_width(bus_width), .widget_type(ff)) apb(

.pclk(pclk),

.presetn(presetn),

.pwdata(pwdata),

.paddr(paddr),

.prdata(prdata),

.pready(pready),

.pwrite(pwrite),

.pprot(pprot),

.pprot_i(pprot_i),

In case the following properties are applied in the specification:

  1. Widget=apb:ff:apb_widget
  2. reset_type=asyncThe RTL output now generated will have the following changes:…apb_widget #(.addr_width(addr_width), .bus_width(bus_width), .reset_type(async), .widget_type(ff)) apb(.pclk(pclk),.presetn(presetn),.pwdata(pwdata),

    .paddr(paddr),

    .prdata(prdata),

    .pready(pready),

    .pwrite(pwrite),

    .pprot(pprot),

    .pprot_i(pprot_i),

  3. Shown below is the snippet of the modified APB widget format
module apb_widget #(parameter addr_width = ‘d32, parameter bus_width = ‘d32, parameter widget_type=default, parameter reset_type=async)
(
// clk and reset
input pclk,                                        //Rising Edge Clock                                  : M
input presetn,                                  //Active LOW Reset                                   : M

// apb master
input [addr_width-1 : 0] paddr,         //Address                                                 : M
input [2:0] pprot,                            //Protection type                                       : M
input psel,                                       //Select                                                   : M
input penable,                                 //Enable                                                   : M
input pwrite,                                   //Direction                                                : M
input [bus_width-1 :0] pwdata,        //Write data                                              : M
input [bus_width/8-1 : 0] pstrb,       //Write strobes : Moutput pready, //Ready   : S
output [bus_width-1 :0] prdata,       //Read Data                                              : S
output pslverr,                                //error                                                      : S

//Proprietary bus signals
output clk,                                              // :S
output reset_l,                                        // :S
output wire [addr_width-1:0] address,      // :S
output wire[bus_width-1:0] wr_data,        // :S
output wire wr_stb,                                 // :S
output wire rd_stb,                                 // :S

input [bus_width-1:0] rd_data,                // :S
input request,                                         // :S
input rd_data_vld,                                   // :S
output wire[bus_width/8-1:0] byte_enb,   // :S
input rd_wait,                                         // :S
output [2:0] pprot_i,
input error
);

 

generate

if(widget_type == ff)

begin : apb_widget_ff

apb_widget_ff #(.addr_width(addr_width), .bus_width(bus_width)) apb(

.pclk(pclk),

.presetn(presetn),

.paddr(paddr),

.pprot(pprot),

.psel(psel),

.penable(penable),

.pwrite(pwrite),

.pwdata(pwdata),

.pstrb(pstrb),

.pready(pready),

.prdata(prdata),

.pslverr(pslverr),

.clk(clk),

.reset_l(reset_l),

.address(address),

.wr_data(wr_data),

.wr_stb(wr_stb),

.rd_stb(rd_stb),

.rd_data(rd_data),

.request(request),

.rd_data_vld(rd_data_vld),

.byte_enb(byte_enb),

.rd_wait(rd_wait),

.pprot_i(pprot_i),

.error(error));

end

else if(widget_type == pulse_pready)

begin : apb_widget_pulse_pready

apb_widget_pulse_pready #(.addr_width(addr_width), .bus_width(bus_width)) apb(

.pclk(pclk),

.presetn(presetn),

.paddr(paddr),

.pprot(pprot),

.psel(psel),

.penable(penable),

.pwrite(pwrite),

.pwdata(pwdata),

.pstrb(pstrb),

.pready(pready),

.prdata(prdata),

.pslverr(pslverr),

.clk(clk),

.reset_l(reset_l),

.address(address),

.wr_data(wr_data),

.wr_stb(wr_stb),

.rd_stb(rd_stb),

.rd_data(rd_data),

.request(request),

.rd_data_vld(rd_data_vld),

.byte_enb(byte_enb),

.rd_wait(rd_wait),

.pprot_i(pprot_i),

.error(error));

end

else

begin : apb_widget_default

apb_widget_default #(.addr_width(addr_width), .bus_width(bus_width),    .reset_type (reset_type)) apb(

.pclk(pclk),

.presetn(presetn),

.paddr(paddr),

.pprot(pprot),

.psel(psel),

.penable(penable),

.pwrite(pwrite),

.pwdata(pwdata),

.pstrb(pstrb),

.pready(pready),

.prdata(prdata),

.pslverr(pslverr),

.clk(clk),

.reset_l(reset_l),

.address(address),

.wr_data(wr_data),

.wr_stb(wr_stb),

.rd_stb(rd_stb),

.rd_data(rd_data),

.request(request),

.rd_data_vld(rd_data_vld),

.byte_enb(byte_enb),

.rd_wait(rd_wait),

.pprot_i(pprot_i),

.error(error));

end

endgenerate

endmodule

Summary

Some bus protocols have multiple widgets with different functionalities. All the various versions of one bus protocol are combined into a single file and selection is based on parameters. Widgets contain `defines for sync and async functionality;  these `defines/macros for reset functionality have been replaced with parameters for the selection of sync/async behaviour  for the bus widgets. The corresponding widget will be automatically generated in the output directory. The enhancement also avoids unnecessary download from the supporting files link and helps keep things consistent and manageable.

Chip-Inside-Chip Flow 

Introduction

IDesignSpecTM supports multiple design hierarchies like “block”, “chip”, “board”, and “system” to enable different architectural design flows. These act as a hierarchy of containers. A block can contain registers. A chip can contain other blocks and provide an aggregator for the blocks. A board is a container for multiple chips and similar to a chip in the generated outputs.

A large SoC design often requires multiple levels of hierarchies to place or group different block IPs, so that these block hierarchies can be placed at different offsets in an address map or can even be in different address maps from both design and verification points of view.

Currently IDesignSpecTM supports both Verilog and UVM outputs until “board” level, which means the top level can be a “board” container, which can have multiple “chip” containers which in turn can have multiple blocks. This limits the maximum number of design hierarchies over block level to two, i.e., “chip” and “board”. These hierarchies act as an aggregator for the underlying hierarchies and do not directly contain any registers. So required aggregation logic is generated at chip/board level in Verilog RTL while in a UVM flow they are implemented by “uvm_reg_block” classes.

To remove the limitation on the number of hierarchies over block level, chip-inside-chip is supported with input SystemRDL from IDS Release v7.0.0.0. With this enhancement the chip can now be a container for other chip hierarchies in addition to block hierarchies.

The property : “chip”

Chip-Inside-Chip flow is supported using the property “chip=true”. For example, in the case of a top chip, and a SystemRDL specification having multiple addrmap (container) hierarchies, addrmaps having property “chip=true” would be considered as chips and required register bus aggregation logic would be generated for them. In the case of UVM they would be implemented using a “uvm_reg_block” class.  The remaining addrmaps would be considered as blocks, as before.

Example:

property chip { type = boolean; component = addrmap; };

addrmap ChipTop{

name = “ChipTop Address Map”;

addrmap Chip_hier2_1{

name = “Chip_hier2_1 Address Map”;

chip = true;

addrmap Chip_hier1_1 {

name = “Chip_hier1_1 Address Map”;

 chip = true;   

addrmap Block1 {

…………………….

};

addrmap Block2 {

…………………….

};

Block1  Block1;

Block2  Block2[2];

};

addrmap OutBlock_hier1_1 {

name  = “OutBlock_hier1_1 Address Map”;

…………………….

};

Chip_hier1_1     Chip_hier1_1_Inst;

OutBlock_hier1_1 OutBlock_hier1_1_Inst;

};

addrmap Chip_hier2_2 {

name = “Chip_hier1_2 Address Map”;

chip = true;

…………………….

};

Chip_hier2_1 Chip_hier2_1_Inst;

Chip_hier2_2 Chip_hier2_2_Inst;

};

The above example shows three levels of chip hierarchies, ChipTop, Chip_hier2_1/Chip_hier2_2, and Chip_hier1_1, supported using “chip=true” property (see Figure 1).

Sample IDSBatch command line for the above example:

idsbatch <sample input RDL file> -top “chip:ChipTop” -out “verilog uvm” -bus <register bus name> -if -preserve -dir <output directory name>

1-1

Figure 1: Chip-Inside-Chip

More details on aggregation logic: https://www.agnisys.com/release/docs/ids/AggregationLogic.html

Using “bus” property

Hierarchical property “bus=<valid register bus name>” can be applied on chip components (aggregators) to overwrite the default bus on these components. This property can be used in scenarios where the top aggregator logic is on a different bus w.r.t to the child aggregator (layered bus logic) connected through a bridge. The top hierarchy will be on the default register bus specified by the “-bus” command line switch in the case of IDSBatch and by the configuration template in the case of IDS GUI.

Example:

property chip { type = boolean; component = addrmap; };

property bus {type = string; component = addrmap;};

addrmap ChipTop{

name = “ChipTop Address Map”;

addrmap Chip_hier2_1{

name = “Chip_hier2_1 Address Map”;

chip = true;

bus = “apb”;

addrmap Chip_hier1_1 {

name = “Chip_hier1_1 Address Map”;

chip = true;

addrmap Block1 {

…………………….

};

addrmap Block2 {

…………………….

};

Block1  Block1;

Block2  Block2[2];

};

addrmap OutBlock_hier1_1 {

name  = “OutBlock_hier1_1 Address Map”;

…………………….

};

Chip_hier1_1     Chip_hier1_1_Inst;

OutBlock_hier1_1 OutBlock_hier1_1_Inst;

};

addrmap Chip_hier2_2 {

name = “Chip_hier1_2 Address Map”;

chip = true;

…………………….

};

Chip_hier2_1 Chip_hier2_1_Inst;

Chip_hier2_2 Chip_hier2_2_Inst;

};

In the above example, the register bus for the chip component hierarchy “Chip_hier2_1” is changed to APB using property “bus=apb”. All the components inside “Chip_hier2_1” will now be on the APB bus interface. The rest of the specification would remain on the default register bus (see Figure 2).

2-1

Figure 2:Chip-Inside-Chip flow with bus property

Limitation

  1. Chip-Inside-Chip flow is currently supported for SystemRDL input format.
  2. This flow is designed to support SoCs having any number of aggregation hierarchies, with the possibility of having different register buses at different levels. So, to enable users to use/reuse the generated aggregator and block RTL logic, chip-inside-chip is only supported with the IDesignSpecTM multi-file output option (-if) in case the generation command contains Verilog output.