使用SystemC建模SystemVerilog状态机的实例

发布时间 2023-12-13 21:47:41作者: sasasatori

通过一个状态机的例子可以比较好的理解SystemC怎么建模RTL。

我们以一个典型的SystemVerilog编写的状态机为例。

fsm.sv:

module fsm( 
    input clk,
    input rst_n,
    input [1:0] in,
    output logic [1:0] out
);

enum logic [1:0] {
    IDLE = 2'b00,
    RUN  = 2'b01,
    STOP = 2'b10
} current_state, next_state;

always_ff @( posedge clk, negedge rst_n ) begin : state_update
    if (!rst_n)
        current_state <= IDLE;
    else
        current_state <= next_state;
end

always_comb begin : state_trans
    next_state = current_state;
    case (current_state)
        IDLE: if(in==2'b01) next_state = RUN;
        RUN: if(in==2'b10) next_state = STOP;
        STOP: if(in==2'b11) next_state = IDLE;
    endcase 
end

always_comb begin : set_output
    out = 2'b00;
    case (current_state)
        IDLE: out = 2'b01;
        RUN: out = 2'b10;
        STOP: out = 2'b11; 
    endcase
end

endmodule

tb_fsm.sv:

`timescale 1ns/1ns
module tb_fsm;

logic clk;
logic rst_n;
logic [1:0] in;
logic [1:0] out;

initial begin
    clk = 1'b1;
    rst_n = 1'b0;
    in = 2'b00;
    #0;
    rst_n = 1'b1;
    #2;
    in = 2'b01;
    #2;
    in = 2'b10;
    #2;
    in = 2'b11;
    #2;
    $finish;
end

always #1 clk = ~clk;

fsm u_fsm_0 (
    .clk(clk),
    .rst_n(rst_n),
    .in(in),
    .out(out)
);

initial begin
    $fsdbDumpfile("tb_fsm.fsdb");
    $fsdbDumpvars;
end

endmodule

运行结果:

image

我们编写等效的SystemC模型:

fsm.h:

#ifndef _FSM_H_
#define _FSM_H_

#include <systemc.h>
#include <iostream>

SC_MODULE(fsm) {
    sc_in<bool> clk;
    sc_in<bool> rst_n;
    sc_in<sc_uint<2>> in;
    sc_out<sc_uint<2>> out;

    void state_update();
    void state_trans();
    void set_output();
    void trace();

    SC_CTOR(fsm)
    {
        SC_METHOD(state_update);
        sensitive << clk.pos() << rst_n.neg();
        SC_METHOD(state_trans);
        sensitive << in << current_state;
        SC_METHOD(set_output);
        sensitive << current_state;
        SC_METHOD(trace);
        sensitive << clk.pos();
    }

    enum state {IDLE=0,RUN=1,STOP=2};

    sc_signal<sc_uint<2>> current_state;
    sc_signal<sc_uint<2>> next_state;
};

#endif

fsm.cpp:

#include <fsm.h>

using namespace std;

void fsm::state_update()
{
    if(rst_n == false)
        current_state = IDLE;
    else
        current_state = next_state;
}

void fsm::state_trans()
{
    switch (current_state.read())
    {
    case IDLE:
        if(in.read() == 1) next_state = RUN;
        break;
    case RUN:
        if(in.read() == 2) next_state = STOP;
        break;
    case STOP:
        if(in.read() == 3) next_state = IDLE;
        break;
    default:
        next_state = current_state;
        break;
    }
}

void fsm::set_output()
{
    switch (current_state.read())
    {
    case IDLE:
        out = 1;
        break;
    case RUN:
        out = 2;
        break;
    case STOP:
        out = 3;
        break;
    default:
        out = 0;
        break;
    }
}

void fsm::trace()
{
    cout << sc_time_stamp() << " in: " << in << " current_state: " << current_state << " next_state: " << next_state << " out: " << out << endl;
}

tb_fsm.cpp

#include "fsm.h"

SC_MODULE(tb_fsm) {
  sc_signal<bool> rst_n;
  sc_signal<sc_uint<2>> in;
  sc_signal<sc_uint<2>> out;
  void test()
  {
    rst_n = 0;
    in = 0;
    wait(SC_ZERO_TIME);
    rst_n = 1;
    wait(3,SC_NS);
    in = 1;
    wait(2,SC_NS);
    in = 2;
    wait(2,SC_NS);
    in = 3;
  }
  SC_CTOR(tb_fsm)
  {
    SC_THREAD(test);
  }
};

int sc_main(int, char*[]) {
  sc_clock clk("clk", 2, SC_NS);

  tb_fsm u_tb_fsm("u_tb_fsm");
  fsm u_fsm("u_fsm"); 

  u_fsm.clk(clk);
  u_fsm.rst_n(u_tb_fsm.rst_n);
  u_fsm.in(u_tb_fsm.in);
  u_fsm.out(u_tb_fsm.out);

  sc_trace_file* file = sc_create_vcd_trace_file("tb_fsm");
  sc_trace(file,u_fsm.clk,"clk");
  sc_trace(file,u_fsm.rst_n,"rst_n");
  sc_trace(file,u_fsm.in,"in");
  sc_trace(file,u_fsm.out,"out");
  sc_trace(file,u_fsm.current_state,"current_state");
  sc_trace(file,u_fsm.next_state,"next_state");
  sc_start(12, SC_NS);
  sc_close_vcd_trace_file(file);
  return 0;
}

运行结果:

0 s in: 0 current_state: 0 next_state: 0 out: 0
0 s in: 0 current_state: 0 next_state: 0 out: 1

Info: (I702) default timescale unit used for tracing: 1 ps (tb_fsm.vcd)
2 ns in: 0 current_state: 0 next_state: 0 out: 1
4 ns in: 1 current_state: 0 next_state: 1 out: 1
6 ns in: 2 current_state: 1 next_state: 2 out: 2
8 ns in: 3 current_state: 2 next_state: 0 out: 3
10 ns in: 3 current_state: 0 next_state: 0 out: 1

用vcd2fsdb将生成的vcd波形文件转换成fsdb格式:
image

fsdb导入到verdi:

image

可以看到和sv的结果一致,注意sc的默认最小仿真时间单位为1ps。

我们总结规律就可以知道,SystemVerilog里面的端口部分就对应SystemC里面用sc_port描述的部分,SystemVerilog里面的信号就对应SystemC里面用sc_signal描述的部分,SystemVerilog里面的always块对应SystemC里面注册的SC_METHOD,SystemVerilog里面例化对应SystemC创建一个module的对象,然后port连接对应的signal即可。