Synchronous Processor
From Lyra
Contents |
Introduction
This example presents the modeling of a MIPS like simple processor using synchronous design style.
Modeling
The synchronous model has a classical 5-stage rigid pipeline. Each stage is modeled with a single-state process. Rendezvous used to communicate with previous and next stages are conjunctly composed as one transition in the current stage. Data hazards are handeled by checking and forwarding data with shared variables among processes, while control hazards are resolved by using rendezvous to control instruction flushing.
A graphical representation of the processes is given below to help readers visualize the code. Some of the transition edges are omitted in the figure for easy visualization and description.
CPU
module cpu (oi < uint<10>, uint<32> > fetch,
oi < uint<10>, uint<32> > load,
out <(uint<10>, uint<32>)> store)
{
rendv decode;
rendv execute;
rendv access_mem;
rendv write_back;
rendv rs, rt;
barrier rd;
barrier jump(10);
reg uint<10> pc;
reg uint<32> instr;
reg (uint<6>, uint<32>, uint<32>, uint<5>, uint<5>, uint<5>, uint<10>) dec_reg;
reg (uint<6>, uint<32>, uint<5>, uint<10>) exe_reg;
reg uint<5> exe_rs, exe_rt;
reg (uint<6>, uint<32>, uint<5>) mem_reg;
init
{
pc = 0;
}
fsm IF_stage(out < uint<32> > decode, in < uint<10> > jump)
{
init S0:
{
when (fetch, decode)
{
fetch.write(pc);
decode.write(instr);
when (jump)
{
instr = 0;
pc = jump.read();
goto S0;
}
instr = fetch.read();
pc = pc + 1;
goto S0;
}
}
}
fsm ID_stage(in < uint<32> > decode, in <uint<10> > jump, in <(uint<5>, uint<32>) >rd,
out <(uint<6>, uint<32>, uint<32>, uint<5>, uint<5>, uint<5>, uint<10>)> execute,
oi <uint<5>, uint<32> > rs, oi <uint<5>, uint<32> > rt)
{
init S0:
{
val (uint<5>, uint<32>) bypass;
val uint<32> opa, opb;
when (decode, execute)
{
// old instruction goes out
execute.write(dec_reg);
when (jump)
{
dec_reg = (0, 0, 0, 0, 0, 0, 0);
when(rd)
{
goto S0;
}
goto S0;
}
when (rs, rt)
{
val uint<6> opcode;
val uint<32> inst;
inst = decode.read();
opcode = inst{31:26};
rs.write(inst{25:21});
rt.write(inst{20:16});
when (rd)
{
bypass = rd.read();
opa = (bypass.$1 == inst{25:21}) ? bypass.$2 : rs.read();
opb = (bypass.$1 == inst{20:16}) ? bypass.$2 : rt.read();
dec_reg = (opcode, opa, opb, inst{25:21}, inst{20:16}, inst{15:11}, inst{9:0});
goto S0;
}
dec_reg = (opcode, rs.read(), rt.read(), inst{25:21}, inst{20:16},
inst{15:11}, inst{9:0});
goto S0;
}
}
}
}
fsm EX_stage (in <(uint<6>, uint<32>, uint<32>, uint<5>, uint<5>, uint<5>, uint<10>)> execute,
out <(uint<6>, uint<32>, uint<5>, uint<10>)> access_mem, out <uint<10> > jump)
{
val uint<6> opcode;
val uint<32> opa, opb;
val uint<32> va, vb, res;
val uint<5> rs_addr, rt_addr, rd_addr;
val uint<10> imm;
init S0:
{
when (execute, access_mem)
{
opcode = execute.read().$1;
rs_addr = execute.read().$4;
rt_addr = execute.read().$5;
rd_addr = execute.read().$6;
imm = execute.read().$7;
//data forwarding
opa = (exe_reg.$1 !=4 && exe_reg.$1 != 5 && exe_reg.$1 != 0 &&
exe_reg.$3 == rs_addr) ? exe_reg.$2 : execute.read().$2;
opb = (exe_reg.$1 !=4 && exe_reg.$1 != 5 && exe_reg.$1 != 0 &&
exe_reg.$3 == rt_addr) ? exe_reg.$2 : execute.read().$3;
va = (mem_reg.$1 != 4 && mem_reg.$1 != 5 && exe_reg.$1 != 0 &&
mem_reg.$3 == rs_addr && exe_reg.$3 != rs_addr) ? mem_reg.$2 : opa;
vb = (mem_reg.$1 != 4 && mem_reg.$1 != 5 && exe_reg.$1 != 0 &&
mem_reg.$3 == rt_addr && exe_reg.$3 != rt_addr) ? mem_reg.$2 : opb;
exe_reg = (opcode, res, rd_addr, imm);
exe_rs = rs_addr;
exe_rt = rt_addr;
access_mem.write(exe_reg);
switch (opcode)
{
case 1://add
{
res = va + vb;
goto S0;
}
case 2: //sub
{
res = va - vb;
goto S0;
}
case 3: //load
{
res = 0;
goto S0;
}
case 4: //store
{
res = vb;
goto S0;
}
case 5: //beq
{
when (jump, va == vb)
{
jump.write(imm);
res = 0;
goto S0;
}
when (va != vb)
{
res = 0;
goto S0;
}
}
case 6: //multiply
{
res = va * vb;
goto S0;
}
default:
{
res = 0;
goto S0;
}
}
}
}
}
fsm MEM_stage (in <(uint<6>, uint<32>, uint<5>, uint<10>)> access_mem,
out <(uint<6>, uint<32>, uint<5>)> write_back)
{
val uint<6> opcode;
val uint<32> res;
val uint<32> wbdata;
val uint<5> rd_addr;
val uint<10> imm;
init S0:
{
when (access_mem, write_back)
{
(opcode, res, rd_addr, imm) = access_mem.read();
mem_reg = (opcode, wbdata, rd_addr);
write_back.write(mem_reg);
switch (opcode)
{
case 3: //load
when (load)
{
load.write(imm);
wbdata = load.read();
goto S0;
}
case 4: //store
when (store)
{
wbdata = 0;
store.write(imm, res);
goto S0;
}
default:
{
wbdata = res;
goto S0;
}
}
}
}
}
fsm WB_stage (in <(uint<6>, uint<32>, uint<5>)> write_back, out <(uint<5>, uint<32>)> rd)
{
val uint<6> opcode;
val uint<32> wbdata;
val uint<5> rd_addr;
init S0:
{
when (write_back)
{
(opcode, wbdata, rd_addr) = write_back.read();
switch (opcode)
{
case 1:
when (rd)
{
rd.write(rd_addr, wbdata);
goto S0;
}
case 2:
when (rd)
{
rd.write(rd_addr, wbdata);
goto S0;
}
case 3:
when (rd)
{
rd.write(rd_addr, wbdata);
goto S0;
}
case 6:
when (rd)
{
rd.write(rd_addr, wbdata);
goto S0;
}
default: goto S0;
}
}
}
}
regfile reg_file(rs, rt, rd);
}
module regfile
(io <uint<5>, uint<32> > rs,
io <uint<5>, uint<32> > rt,
in <(uint<5>, uint<32>)> rd)
{
/*state variables*/
reg uint<32>[32] regfile; //32 32-bit registers
init
{
regfile = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
}
fsm rs_read {
init idle:
when (rs)
{
rs.write(regfile[rs.read()]);
goto idle;
}
}
fsm rt_read {
init idle:
when (rt)
{
rt.write(regfile[rt.read()]);
goto idle;
}
}
fsm rd_write {
init idle:
when (rd)
{
val (uint<5>, uint<32>) vrd;
vrd = rd.read();
regfile[vrd.$1] = vrd.$2;
goto idle;
}
}
}
Memory
module memory ( io <uint<10>, uint<32> > fetch,
io <uint<10>, uint<32> > load,
in <(uint<10>,uint<32>)> store)
{
reg uint<32>[1024] ram;
init
{
/*
sub r0, r0, r0;
load r1, [128]; #1
load r2, [129]; #11
load r3, [130]; #1
loop:
add r0, r0, r1;
add r1, r1, r3;
beq r1, r2, finish;
beq r0, r0, loop;
finish:
store r0, [131]
beq r0, r0, finish;
*/
ram[0] = 32'd2 << 26;
ram[1] = (32'd3 << 26) | (32'd1 << 11) | 128;
ram[2] = (32'd3 << 26) | (32'd2 << 11) | 129;
ram[3] = (32'd3 << 26) | (32'd3 << 11) | 130 ;
ram[4] = (32'd1 << 26) | (32'd1 << 16);
ram[5] = (32'd1 << 26) | (32'd1 << 21) | (32'd3 << 16) | (32'd1 << 11);
ram[6] = (32'd5 << 26) | (32'd1 << 21) | (32'd2 << 16) | 8;
ram[7] = (32'd5 << 26) | 4;
ram[8] = (32'd4 << 26) | 131;
ram[9] = (32'd5 << 26) | 8;
ram[128] = 1;
ram[129] = 11;
ram[130] = 1;
}
fsm inst
{
val uint<10> address; //10 bit value for address
init ready: //->in_read:
/* memory read begin */
when (fetch)
{
address = fetch.read();
fetch.write(ram[address]);
goto ready;
}
}
fsm data
{
val uint<10> address; //10 bit value for address
val uint<32> inval;
init ready: //->in_read:
/* memory read begin */
when (load)
{
address = load.read();
load.write(ram[address]);
goto ready;
}
when (store)
{
(address, inval) = store.read();
ram[address] = inval;
goto ready;
}
}
}
toplevel
#include "librfsm.rfm"
#include "cpu.rfm"
#include "memory.rfm"
module toplevel
{
rendv fetch, load, store;
cpu mycpu(fetch, load, store);
memory mymem(fetch, load, store);
}



