Verilator Unleashed – Mixed software and hardware simulation – Capitole du libre - 15 Novembre 2014



Verilator Unleashed – Mixed software and hardware simulation – Capitole du libre - 15 Novembre 2014

0 0


cdl2014-verilator

Slides of my conference "Verilator Unleashed" at Capitole du Libre 2014.

On Github Torlus / cdl2014-verilator

Verilator Unleashed

Mixed software and hardware simulation

Capitole du libre - 15 Novembre 2014

Grégory Estrade - @Torlus sur Twitter, GitHub.

Frédéric Requin.

Motivations et réalisations

Grégory Estrade

Reverse-engineering de l'Atari Jaguar.

  • Travaux autour du source des ASICs.
  • "Glue Logic": étude des schémas.

Frédéric Requin

Réalisation d'un core Amiga 500.

  • Ecriture d'un core 68000 optimisé.
  • Travaux autour de l'outillage (Verilator).

Carte d'extension (I/O) pour l'Altera NDK - Stratix-II Edition.

Etapes

  • "Verilation" d'un SoC de test
  • Simulation du SoC, 100% Verilog.
  • Ajout de traces (VCD).
  • Simulation du SoC, avec parties en C++.
  • Intégration du désassembleur 68000 de Musashi.
  • Simulation et vérification conjointe avec Musashi.

Présentation du J68

  • Design inspiré du core J1.
  • Stack-based processor:
    • Microcode stocké dans une ROM interne.
    • 2 stacks: Data stack et Return stack.
    • Microcode inspiré du langage Forth.
  • Avantages: peu de ressources utilisées, vitesse.
  • Inconvénients: "cycle accuracy" non atteint.

Présentation du J68

Exemple de microcode

///////////////////////////////////////
// 0000.000000.xxxxxx : ORI.B opcode //
///////////////////////////////////////
Op_ORIB:
	LDW    (PC)+                 // Read immediate value
	CALL   EA1_Read_B            // Read byte from effective address
	ORB.                         // OR it with the immediate value
	FLAG   -**00,CIN=CLR         // Update N and Z, clear V and C
	JUMP   EA1_Update_B          // Write back result

SoC de test

Déclaration en Verilog

module j68_soc
(
    input         rst_n,
    input         clk,

    input         uart_rxd,
    input         uart_cts_n,
    input         uart_dcd_n,
    output        uart_txd,
    output        uart_rts_n,
    ...
);

Compilation

verilator $TOP_FILE.v $COMPILE_OPT $TRACE_OPT -top-module $TOP_FILE -exe $CPP_FILES
cd ./obj_dir
make -j -f V$TOP_FILE.mk V$TOP_FILE
cd ..

SoC de test - Verilated

Instantiation du modèle

int main(int argc, char **argv, char **env)
{
    ...
    Verilated::commandArgs(argc, argv);
    Vj68_soc* top = new Vj68_soc;
    ...
    while (tb_sstep < NUM_STEPS)
    {
        top->rst_n = (tb_sstep < (vluint64_t)24) ? 0 : 1;
        top->clk = top->clk ^ 1;
        top->eval();
        if (Verilated::gotFinish()) break;
    }
    top->final();
    ...
}

SoC de test - Verilated

Ajout de traces

#if VM_TRACE
    // Init VCD trace dump
    Verilated::traceEverOn(true);
    VerilatedVcdC* tfp = new VerilatedVcdC;
    top->trace(tfp, 99);
    tfp->spTrace()->set_time_resolution("1 ps");
    tfp->open(file_name);
#endif
...
while (tb_sstep < NUM_STEPS)
{
    ...
    top->eval();
#if VM_TRACE
    tfp->dump(tb_time);
#endif
}

SoC de test - Verilated

Visualisation des traces avec GTKWave

SoC de test - Simulation

Testbench/Simulation en Verilog

// Inferred block RAM
reg  [15:0] r_mem_blk [0:(1 << ADDR_WIDTH) - 1];

reg  [15:0] r_q;

// Read / write
always@(posedge clock) begin
  r_q <= r_mem_blk[address][15:0];
  if (wren) begin
    if (byteena[0]) r_mem_blk[address][7:0]  <= data[7:0];
    if (byteena[1]) r_mem_blk[address][15:8] <= data[15:8];
  end
end

assign q = r_q;

SoC de test - Simulation

Testbench/Simulation en C++ (1/2)

// Constructor
CART::CART(int w, bool debug, int size)
// Destructor
CART::~CART()
// Binary file loading
void CART::load(const char *name, vluint32_t begin, vluint32_t end)

// Cycle evaluate
void CART::eval(
  vluint64_t cycle, vluint8_t clk, // Cycle counter, clock
  vluint8_t ce_n, vluint8_t oe_n, // Control signals
  vluint32_t a, // Address
  vluint32_t &q, vluint8_t &oe // Outputs: data, output enable
)

SoC de test - Simulation

Testbench/Simulation en C++ (2/2)

if (!clk) return;
a &= 0x7fffff;
switch(width) {
  case 1:	// 16 bits
    a &= ~1;
    q = (mem_array[a] << 8) | mem_array[a + 1];
    break;
  case 2: // 32 bits
    a &= ~3;
    q = (mem_array[a] << 24) | (mem_array[a + 1] << 16)
      | (mem_array[a + 2] << 8) | (mem_array[a + 3] << 0);
    break;
  default:
    q = mem_array[a];
}
oe = (!ce_n && !(oe_n & 1)) ? 1 : 0;
oe |= (!ce_n && !(oe_n & 2)) ? 2 : 0;

Musashi

Présentation

  • Simulation des processeurs 68000 à 68020.
  • Ecrit en C, par Karl Stenerud.
  • Utilisé par le projet MAME.

Intégration

  • Utilisation du désassembleur.
  • Execution conjointe J68 - Musashi.

J68 & Musashi

(System)Verilog DPI - Direct Programming Interface

Déclaration

import "DPI-C" function void dpi_trace_init();
import "DPI-C" function dpi_trace_fetch(
  input integer sr,
  input integer pc,
  input integer usp,
  input integer ssp,
  input integer lvl
);

Utilisation

always@(posedge clk)
begin
  // Instruction fetch
  if (w_dbg_ifetch)
    dpi_trace_fetch({16'd0, w_dbg_sr_reg}, w_dbg_pc_reg, w_dbg_usp_reg,
      w_dbg_ssp_reg, {29'd0, w_dbg_irq_lvl});
  ...
end

J68 & Musashi

Utilisation du désassembleur

#include "svdpi.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "musashi/m68k.h"

void dpi_trace_fetch(int sr, int pc, int usp, int ssp, int lvl)
{
  ...
  m68k_disassemble(dis_buff, prev_pc, M68K_CPU_TYPE_68010);
  fprintf(fh_j, "PC=%08lX  %s\n", prev_pc, dis_buff);
  fprintf(fh_j, "D0=%08X %08X %08X %08X %08X %08X %08X %08X \n",
    regs[0], regs[1],  regs[2],  regs[3],  regs[4],  regs[5],
    regs[6],  regs[7]);
  ...
}

J68 & Musashi

Exécution conjointe

reg = m68k_get_reg((void *)NULL, M68K_REG_PC);
m68k_disassemble(dis_buff, reg, M68K_CPU_TYPE_68010);
fprintf(fh_m, "PC=%08lX  %s\n", reg, dis_buff);

/* Musashi call */
m68k_execute(1);

/* Dump registers */
fprintf(fh_m, "D0=");
for (i = (int)M68K_REG_D0; i <= (int)M68K_REG_D7; i++)
{
  reg = m68k_get_reg((void *)NULL, (m68k_register_t)i);
  fprintf(fh_m, "%08lX ", reg);
}

J68 & Musashi

Exemple de traces produites

PC=000002F0  movea.l A0,A1
D0=11552299 33774401 00000000 00000000 00000000 00000000 00000000 00000000
A0=00008004 00008004 00000000 00000000 00000000 00000000 00000000 0000FFFC
USP=00000000 SSP=0000FFFC SR=2710 XNZVC=10000

PC=000002F2  move.l  #$09010101,(A1)+
D0=11552299 33774401 00000000 00000000 00000000 00000000 00000000 00000000
A0=00008004 00008008 00000000 00000000 00000000 00000000 00000000 0000FFFC
USP=00000000 SSP=0000FFFC SR=2710 XNZVC=10000

PC=000002F8  abcd    -(A0),-(A1)
D0=11552299 33774401 00000000 00000000 00000000 00000000 00000000 00000000
A0=00008003 00008007 00000000 00000000 00000000 00000000 00000000 0000FFFC
USP=00000000 SSP=0000FFFC SR=2711 XNZVC=10001

Conclusion

Des avantages à tous niveaux

  • Vitesse d'exécution.
  • Confort d'utilisation.
  • Intégration simplifiée.

Best of both worlds!

Questions?

Grégory Estrade - @Torlus sur Twitter, GitHub.

Frédéric Requin.