An Introduction to using BLADE with SystemC¶
BLADE allows two primary types of modules to be defined, ones which are leaf nodes (IMP modules), and modules which instance leaf nodes (e.g. wiring hierarchy / levels).
IMP Modules¶
BLADE generates a base class for your System-C module with boiler plate code for a single clocked process sensitive to the default (implicit) clk and rst (asynchronous active high).
The designer is responsible for providing the implementation (derived child class) for the module.
Module Definition¶
Here is a example IMP (leaf) module in YAML:
# His
#include "bit.yaml"
##############################################################
# Module
##############################################################
- !Mod
name: my_block
options: [IMP]
##############################################################
# Ports
##############################################################
ports:
- !HisRef [my_output, bit, 'Output called m_my_output_out[0]', 1, Master]
By default (unless using the module option NO_CLK_RST or an explicitly named clk/rst), this module will have an implicit clock and reset called ‘clk’ and ‘rst’.
The equivalent YAML expressing BLADE’s internal view of this module is below (and will generate the same module as above);
# His
#include "clock.yaml"
#include "reset.yaml"
#include "bit.yaml"
##############################################################
# Module
##############################################################
- !Mod
name: my_block
options: [IMP]
##############################################################
# Ports
##############################################################
ports:
- !HisRef [clk, clock, 'Clock called clk', 1, Slave, '', [EXPLICIT_NAME, AUTO_CLK]]
- !HisRef [rst, reset, 'Reset called rst', 1, Slave, '', [EXPLICIT_NAME, AUTO_RST]]
- !HisRef [my_output, bit, 'Output called m_my_output_out[0]', 1, Master]
In SYS-C, the base class (generated as gen_c/project/myblock.[cpp/h]) for this module gives the I/O interface and would look like;
class MyBlock: public sc_module
{
sc_in<bool> clk;
sc_in<bool> rst;
sc_out<BitMaster> m_my_output_out[1];
...
};
In Verilog, the module definition would look like;
module MyBlockImp
(
input clk,
input rst
output m_my_output_out_0
);
...
endmodule
Generated Base Class¶
Under the hood, the C++ base class generated by BLADE will define a single SC_CTHREAD (assuming the block has a clock and reset) which will call initialize() on asynchronous active high reset, and run() on every positive edge of the clock.
The simplified equivalent of the generated base class is below:
class MyBlock: public sc_module
{
sc_in<bool> clk;
sc_in<bool> rst;
sc_out<BitMaster> m_my_output_out[1];
MyBlock()
{
SC_CTHREAD(main, clk.pos());
async_reset_signal_is(rst, true);
}
void main()
{
/*[MyBlockImp::]*/initialize();
wait();
while (1)
{
/*[MyBlockImp::]*/run();
wait();
}
}
};
It is important to understand how virtual functions are evaluated in C++, as BLADE makes use of these when calling initialize() and run().
User Specified Implementation¶
For ‘IMP’ modules, the designer must provide some implementation in a derived child class (i.e. one that inherits from the generated base class).
The naming scheme for handwritten IMP filenames and class names is required to follow the rules below;
Files:
/view/src_c/{block_name}_imp.cpp
/view/src_c/{block_name}_imp.h
Module Name:
BlockNameImp
Filename -> Module Name:
- Capitalize words separated by underscores, remove underscores, append Imp
Example¶
Here is an example IMP for our module;
//--------------------------------------------------------------------
// initialize: Reset state
//--------------------------------------------------------------------
void MyBlockImp::initialize(void)
{
MyBlock::initialize();
m_my_output_out[0].write(1);
}
//--------------------------------------------------------------------
// run: Clock process (SC_CTHREAD sensitive to a clock)
//--------------------------------------------------------------------
void MyBlockImp::run(void)
{
MyBlock::run();
m_my_output_out[0].write(~m_my_output_out[0].read());
}
It is important to note that all flops in your design should be reset, and the initialize() function is typically where this must happen (unless, for example, you have created your own SC_CTHREAD).
The run() function is where synchronous logic would be expressed, as this function is invoked on every rising clock edge.
The equivalent Verilog for this module would be;
module MyBlockImp
(
input clk,
input rst
output m_my_output_out_0
);
reg m_my_output_out_0;
always @ (posedge clk or posedge rst)
if (rst)
begin : initialize
m_my_output_out_0 <= 1'b1;
end
else
begin: run
m_my_output_out_0 <= ~m_my_output_out_0;
end
endmodule
Combinational Logic¶
Combinational logic can be expressed in System-C using SC_METHODs.
Any communication between SC_METHODs and SC_CTHREADs must be achieved using sc_signal types, and any state that is read by a SC_METHOD must be present in its sensitivity list.
SC_METHODs can drive module outputs (although this maybe in-advisable for the purposes of good synchronous design principles), as long as these outputs have not been driven by a SC_CTHREAD (e.g. in initialize() or run()).
As with Verilog’s combinatorial ‘always @’ blocks, SC_METHODs should not attempt to maintain state between invocations (i.e. inferring a latch), and for this reason care should be taken to assign sc_signal/sc_out’s in all code paths through the SC_METHOD.
The example IMP has been updated below;
Header¶
class MyBlockImp: public MyBlock
{
public:
SC_HAS_PROCESS(MyBlockImp);
MyBlockImp(sc_module_name name);
sc_signal<bool> m_my_wire;
void my_combo_func(void);
};
Implementation¶
//--------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------
MyBlockImp::MyBlockImp()
{
SC_METHOD(my_combo_func);
sensitive << m_my_output_out[0]; // Add signal to sensitivity list
}
//--------------------------------------------------------------------
// initialize: Reset state
//--------------------------------------------------------------------
void MyBlockImp::initialize(void)
{
MyBlock::initialize();
m_my_output_out[0].write(1);
}
//--------------------------------------------------------------------
// run: Clock process (SC_CTHREAD sensitive to a clock)
//--------------------------------------------------------------------
void MyBlockImp::run(void)
{
MyBlock::run();
m_my_output_out[0].write(m_my_wire.read());
}
//--------------------------------------------------------------------
// my_combo_func: Combinational logic - invoked on m_my_output_out
// update.
//--------------------------------------------------------------------
void MyBlockImp::my_combo_func(void)
{
m_my_wire.write(~m_my_output_out[0].read())
}
Equivalent Verilog¶
module MyBlockImp
(
input clk,
input rst
output m_my_output_out_0
);
reg m_my_wire;
reg m_my_output_out_0;
//--------------------------------------------------------------------
// SC_CTHREAD
//--------------------------------------------------------------------
always @ (posedge clk or posedge rst)
if (rst)
begin : initialize
m_my_output_out_0 <= 1'b1;
end
else
begin: run
m_my_output_out_0 <= m_my_wire;
end
//--------------------------------------------------------------------
// SC_METHOD
//--------------------------------------------------------------------
always @ (m_my_output_out_0)
begin : my_combo_func
m_my_wire = ~m_my_output_out_0;
end
endmodule
Common Pitfalls¶
Common SYS-C / BLADE coding errors include:
Missing reset for state which infers flops. Make sure all state is reset in initialize().
Items missing from sensitivity list of SC_METHOD. Make sure any sc_signals/sc_in/sc_out which are read() in a SC_METHOD are in the sensitivity list.
Multi driven nets. Make sure sc_signals/sc_out’s are only driven in one SC_CTHREAD or SC_METHOD.
Attempting to maintain state within an SC_METHOD. All state written in a SC_METHOD must be written in all code paths and should not be read() from.
Be aware that BLADE resets all module outputs in the base class initialize() call. If you do not want this, for example due to driving an output from a SC_METHOD, set the ‘UNDRIVEN’ directive in the module’s port option.