Help us improve
Share bugs, ideas, or general feedback.
From rtl-agent-team
Enforces SystemC/TLM-2.0 coding standards for BFM and Reference Model development, covering naming conventions, AT/LT patterns, and testbench integration.
npx claudepluginhub babyworm/rtl-agent-team --plugin rtl-agent-teamHow this skill is triggered — by the user, by Claude, or both
Slash command
/rtl-agent-team:systemcThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<Purpose>
Generates SystemC TLM-2.0 Bus Functional Models (BFM) with AMBA protocol extensions (AXI/AHB/APB) for early performance estimation and protocol verification.
Provides SystemVerilog testbench best practices including layered architecture, self-checking TBs, transaction classes, and verification patterns. Use for TB structure and methodology.
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
Target standard: C++17 (-std=c++17).
-std=c++17
<Use_When>
<Do_Not_Use_When>
systemverilog skillrtl-p5s-func-verify skill
</Do_Not_Use_When><Why_This_Exists> BFMs and Reference Models are the golden reference for RTL verification. Following consistent coding standards and TLM-2.0 patterns ensures:
<Execution_Policy>
templates/tlm2-module-template.cpp as the starting point for new modulesexamples/bfm-at-pattern.cpp for AT BFM implementation patternsexamples/bfm-pattern.cpp for LT-style BFM implementation patterns (for simple register access)
</Execution_Policy>| Type | Pattern | Example |
|---|---|---|
| Reference Model | ref_{module}.c / .h | ref_cabac.c |
| BFM | bfm_{module}.cpp / .h | bfm_axi_master.cpp |
| TLM Adapter | tlm_{module}_adapter.cpp | tlm_cabac_adapter.cpp |
| Memory Manager | memory_manager.h | memory_manager.h |
| DPI-C Interface | dpi_{module}.cpp / .h | dpi_interface.cpp |
| Testbench Top | tb_{module}_top.cpp | tb_cabac_top.cpp |
| Package (shared types) | {module}_types.h | cabac_types.h |
| Target | Rule | Example |
|---|---|---|
| SC_MODULE | snake_case | cabac_encoder_bfm |
| Reference Model class | {module}_ref_model | cabac_ref_model |
| BFM class | {module}_bfm | axi_master_bfm |
| TLM Socket | {role}_{protocol}_socket | init_axi_socket, targ_mem_socket |
| Member variables | m_ prefix | m_state, m_ctx_table |
| Constants | UPPER_SNAKE_CASE | MAX_CTX_ENTRIES |
SystemC ports use the same names as their RTL counterparts:
sc_in<sc_uint<8>> i_data{"i_data"};
sc_out<bool> o_valid{"o_valid"};
sc_in<bool> sys_clk{"sys_clk"}; // clock: no i_/o_ prefix
sc_in<bool> sys_rst_n{"sys_rst_n"}; // reset: no i_/o_ prefix
class cabac_ref_model {
public:
static uint32_t encode_bin(uint16_t ctx_addr, bool bin_val,
const ctx_table_t& ctx_table);
void process_block(const block_input_t& input, block_output_t& output);
void reset();
private:
ctx_table_t m_ctx_table;
};
int16_t, uint32_t (no int, no float)// CORRECT: bit-exact fixed-point multiply
int32_t fixed_mul(int16_t a, int16_t b) {
return static_cast<int32_t>(a) * static_cast<int32_t>(b);
}
// WRONG: implicit promotion may differ from RTL
int result = a * b; // 'int' width is platform-dependent
nb_transport_fw/bw() (LT only when explicitly requested)| Style | Interface | Use Case |
|---|---|---|
| AT (Approximately Timed) | nb_transport_fw/bw() | DEFAULT. Timing-accurate, pipelined, OoO |
| LT (Loosely Timed) | b_transport() | Fast simulation, simple register access only |
Initiator Target
|-------- BEGIN_REQ ------->|
|<------- END_REQ ----------|
|<------- BEGIN_RESP -------|
|-------- END_RESP -------->|
#include <systemc>
#include <tlm>
#include <tlm_utils/simple_initiator_socket.h>
#include <tlm_utils/simple_target_socket.h>
#include <tlm_utils/peq_with_cb_and_phase.h> // AT phase scheduling
// #include <amba_pv.h> // AMBA protocol extensions (when needed)
┌────────────┐ AT nb_transport ┌────────────┐
│ Testbench │◄──── fw/bw ─────►│ BFM │
│ (Initiator)│ │(Cycle-acc.)│
└────────────┘ └─────┬─────┘
│ Pin Adapter
┌──────▼──────┐
│ RTL Wrapper │ (optional)
│ (sc_signal) │──► DPI-C ──► SV TB
└─────────────┘
Required component for payload reuse in AT models:
class MemoryManager : public tlm::tlm_mm_interface {
public:
tlm::tlm_generic_payload* allocate() {
if (m_pool.empty()) return new tlm::tlm_generic_payload(this);
auto* p = m_pool.back(); m_pool.pop_back(); return p;
}
void free(tlm::tlm_generic_payload* p) override {
p->reset(); m_pool.push_back(p);
}
~MemoryManager() override { for (auto* p : m_pool) delete p; }
private:
std::vector<tlm::tlm_generic_payload*> m_pool;
};
Usage: m_mm.allocate() → trans->acquire() → ... → trans->release()
| Protocol | When to Use |
|---|---|
| AXI | DEFAULT. High-performance, burst, out-of-order |
| AHB | Legacy interconnect, in-order |
| APB | Low-bandwidth peripherals, register access |
| ACE | ONLY when cache coherency is explicitly required |
Default AXI extension setup:
#include <amba_pv.h>
auto* ext = new amba_pv::axi_extension();
ext->set_id(0);
ext->set_burst(amba_pv::AXI_BURST_INCR); // Incrementing (most common)
ext->set_length(burst_len); // AxLEN (beats - 1)
ext->set_size(log2(beat_size)); // AxSIZE
ext->set_cache(0xF); // Write-back, allocate
ext->set_prot(0x0); // Unprivileged, secure, data
trans.set_extension(ext);
See the
<Advanced>section for AHB/APB/ACE details and AXI attribute tables.
# Reference Model (standalone C, no SystemC)
gcc -std=c11 -O2 -Wall -Wextra -Werror -shared -fPIC -o ref_cabac.so ref_cabac.c
# BFM (SystemC required)
g++ -std=c++17 -O2 -Wall -Wextra \
-I${SYSTEMC_HOME}/include -L${SYSTEMC_HOME}/lib-linux64 -lsystemc \
-o tb_cabac tb_cabac_top.cpp bfm_cabac.cpp
# cocotb integration (shared library, C ref model)
gcc -std=c11 -shared -fPIC -o ref_cabac.so ref_cabac.c
cocotb integration:
import ctypes
lib = ctypes.CDLL("./ref_cabac.so")
lib.encode_bin.restype = ctypes.c_uint32
lib.encode_bin.argtypes = [ctypes.c_uint16, ctypes.c_bool]
expected = lib.encode_bin(ctx_addr, bin_val)
<cstdint>), RAII, const, Header guardfloat/double in bit-exact modelsmalloc/free, platform-dependent intusing namespace std; in headersb_transport in performance BFMs (use AT)<Tool_Usage> This skill is not executed directly. It is referenced by agents that generate SystemC code (e.g., bfm-dev, ref-model-dev). Agents should follow the conventions defined here. </Tool_Usage>
AT non-blocking BFM with AXI extension and memory manager: ```cpp void axi_master_bfm::run() { tlm::tlm_generic_payload* trans = m_mm.allocate(); trans->acquire(); // ... setup payload + AXI extension ... tlm::tlm_phase phase = tlm::BEGIN_REQ; sc_core::sc_time delay = sc_core::SC_ZERO_TIME; init_socket->nb_transport_fw(*trans, phase, delay); // ... handle END_REQ, BEGIN_RESP, END_RESP via PEQ ... trans->release(); } ``` Bit-exact ref model with fixed-width integers: ```cpp int32_t cabac_ref_model::encode_bin(uint16_t ctx_addr, bool bin_val) { uint16_t range = m_ctx_table[ctx_addr].range; uint16_t lps = static_cast((range >> 6) & 0x03); // ... bit-exact operations } ``` Float, platform-dependent int, LT in performance BFM: ```cpp float encode_result = ctx_range * 0.5f; // WRONG: float in bit-exact model int lps = range >> 6; // WRONG: 'int' is platform-dependent ```<Escalation_And_Stop_Conditions>
<Final_Checklist>
ref_ / bfm_ / tlm_ / dpi_ prefixint32_t etc., no int/float)i_data, o_valid, sys_clk)m_ prefix for member variables| Type | Value | Description |
|---|---|---|
AXI_BURST_FIXED | 0 | Fixed address (FIFO access) |
AXI_BURST_INCR | 1 | Incrementing address (DEFAULT) |
AXI_BURST_WRAP | 2 | Wrapping burst (cache line) |
0x0: Non-cacheable, non-bufferable (device)0x3: Cacheable, bufferable, no allocate0xF: Write-back, read/write allocate (normal memory)| Code | Description |
|---|---|
AXI_RESP_OKAY | Success |
AXI_RESP_EXOKAY | Exclusive access success |
AXI_RESP_SLVERR | Slave error |
AXI_RESP_DECERR | Decode error (no slave at address) |
auto* ahb_ext = new amba_pv::ahb_extension();
ahb_ext->set_trans(amba_pv::AHB_TRANS_NONSEQ);
ahb_ext->set_burst(amba_pv::AHB_BURST_SINGLE);
ahb_ext->set_size(2); // 4 bytes (2^2)
ahb_ext->set_prot(0x0);
ahb_ext->set_master(0);
trans.set_extension(ahb_ext);
Transfer types: AHB_TRANS_IDLE (0), AHB_TRANS_BUSY (1), AHB_TRANS_NONSEQ (2), AHB_TRANS_SEQ (3)
Burst types: SINGLE, INCR, WRAP4, INCR4, WRAP8, INCR8, WRAP16, INCR16
auto* apb_ext = new amba_pv::apb_extension();
apb_ext->set_prot(0x0);
trans.set_extension(apb_ext);
APB: Single 32-bit transfers only, in-order, 2-phase (SETUP + ACCESS).
Use ACE only when cache coherency is explicitly required:
auto* ace_ext = new amba_pv::ace_extension();
ace_ext->set_domain(amba_pv::ACE_DOMAIN_INNER_SHAREABLE);
ace_ext->set_snoop(amba_pv::ACE_SNOOP_READ_SHARED);
ace_ext->set_barrier(amba_pv::ACE_BARRIER_NORMAL);
ace_ext->set_burst(amba_pv::AXI_BURST_INCR);
ace_ext->set_cache(0xF);
trans.set_extension(ace_ext);
Domain types: NON_SHAREABLE, INNER_SHAREABLE, OUTER_SHAREABLE, SYSTEM
Use only when SystemVerilog co-simulation is needed.
// dpi_interface.h
#ifdef __cplusplus
extern "C" {
#endif
void dpi_sc_init();
void dpi_sc_run(uint64_t time_ps);
void dpi_sc_finish();
int dpi_axi_write(uint64_t addr, const unsigned char* data, unsigned int len);
int dpi_axi_read(uint64_t addr, unsigned char* data, unsigned int len);
extern void sv_notify_completion(int trans_id, int status);
#ifdef __cplusplus
}
#endif
module tb_dpi_cosim;
import "DPI-C" function void dpi_sc_init();
import "DPI-C" function void dpi_sc_run(longint unsigned time_ps);
import "DPI-C" function void dpi_sc_finish();
import "DPI-C" function int dpi_axi_write(
longint unsigned addr, input byte unsigned data[], int unsigned len);
export "DPI-C" function sv_notify_completion;
function void sv_notify_completion(int trans_id, int status);
$display("Transaction %0d completed with status %0d", trans_id, status);
endfunction
initial begin
dpi_sc_init();
dpi_sc_run(100_000); // 100ns
// ... transactions ...
dpi_sc_finish();
$finish;
end
endmodule
Use LT only for simple register access (APB/AXI-Lite) or fast SW simulation:
void my_bfm::b_transport(tlm::tlm_generic_payload& trans, sc_time& delay) {
if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
std::memcpy(&m_memory[addr], data, len);
trans.set_response_status(tlm::TLM_OK_RESPONSE);
} else if (trans.get_command() == tlm::TLM_READ_COMMAND) {
std::memcpy(data, &m_memory[addr], len);
trans.set_response_status(tlm::TLM_OK_RESPONSE);
}
delay += sc_time(m_latency_cycles * m_clk_period_ns, SC_NS);
}
# BFM with AMBA-PV headers
g++ -std=c++17 -O2 -Wall -Wextra \
-I${SYSTEMC_HOME}/include -I${AMBA_PV_HOME}/include \
-L${SYSTEMC_HOME}/lib-linux64 -lsystemc \
-o tb_axi tb_axi_top.cpp bfm_axi_master.cpp
| Category | Forbidden | Correct |
|---|---|---|
| Protocol | LT when AT is specified | Use nb_transport_fw/bw |
| Protocol | ACE when simple AXI suffices | Use AXI by default |
| Timing | Magic numbers: wait(2.0, SC_NS) | Derive from timing_constraints.json |
| Memory | No memory manager for pooled payloads | Use tlm_mm_interface + acquire/release |
| Extensions | Leaking extension memory | Call p->reset() in MemoryManager::free() |
| Phases | Missing phase transitions in AT | Implement all 4 phases with PEQ |
| Verification | Model without testbench | Every model needs sc_main testbench |
| DPI | Blocking calls that deadlock | Queue to SC_THREAD for async handling |
| Response | Missing set_response_status() | Always set before returning |