Commit 30b89667 by Clifford Wolf

Add bigsim/navre

Signed-off-by: Clifford Wolf <clifford@clifford.at>
parent 42963512
SUBDIRS = verific SUBDIRS = verific bigsim
all: $(addsuffix /.stamp,$(SUBDIRS)) all: $(addsuffix /.stamp,$(SUBDIRS))
echo; find * -name "*.status" | sort | xargs grep -H . | sed 's,^, ,; s,.status:,\t,; s,PASS,pass,;' | expand -t40; echo echo; find * -name "*.status" | sort | xargs grep -H . | sed 's,^, ,; s,.status:,\t,; s,PASS,pass,;' | expand -t40; echo
......
/.stamp
/*_cmos.status
/*_ice40.status
/*/work_sim
/*/work_cmos
/*/work_ice40
/*/.stamp_sim
/*/.stamp_cmos
/*/.stamp_ice40
all::
touch .stamp
define template
all:: $(addprefix $(1)/.stamp_,sim $(2))
$(1)/.stamp_sim:
bash run.sh $(1) sim
touch $$@
$(1)/.stamp_cmos: $(1)/.stamp_sim
bash run.sh $(1) cmos
touch $$@
$(1)/.stamp_ice40: $(1)/.stamp_sim
bash run.sh $(1) ice40
touch $$@
clean::
rm -rf $(1)/.stamp_* $(1)/work_*
endef
$(eval $(call template,navre,cmos ice40))
.PHONY: all clean
Larger designs with test benches. We compare pre- and post-synthesis
results for CMOS synthesis and iCE40.
The Navré AVR clone (8-bit RISC)
https://opencores.org/project/navre
TOP="softusb_navre"
RTL="softusb_navre.v"
SIM="bench.v"
/*
* Milkymist SoC
* Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module softusb_navre #(
parameter pmem_width = 11, /* < in 16-bit instructions */
parameter dmem_width = 13 /* < in bytes */
) (
input clk,
input rst,
output reg pmem_ce,
output [pmem_width-1:0] pmem_a,
input [15:0] pmem_d,
output reg dmem_we,
output reg [dmem_width-1:0] dmem_a,
input [7:0] dmem_di,
output reg [7:0] dmem_do,
output reg io_re,
output reg io_we,
output [5:0] io_a,
output [7:0] io_do,
input [7:0] io_di,
input [7:0] irq,
output reg [7:0] irq_ack,
output reg [pmem_width-1:0] dbg_pc
);
/* Register file */
reg [pmem_width-1:0] PC;
reg [7:0] GPR[0:23];
reg [15:0] U; /* < R24-R25 */
reg [15:0] pX; /* < R26-R27 */
reg [15:0] pY; /* < R28-R29 */
reg [15:0] pZ; /* < R30-R31 */
reg I, T, H, S, V, N, Z, C;
/* Stack */
reg [7:0] io_sp;
reg [15:0] SP;
reg push;
reg pop;
always @(posedge clk) begin
if(rst) begin
io_sp <= 8'd0;
`ifndef REGRESS
SP <= 16'd0;
`endif
end else begin
io_sp <= io_a[0] ? SP[7:0] : SP[15:8];
if((io_a == 6'b111101) | (io_a == 6'b111110)) begin
if(io_we) begin
if(io_a[0])
SP[7:0] <= io_do;
else
SP[15:8] <= io_do;
end
end
if(push)
SP <= SP - 16'd1;
if(pop)
SP <= SP + 16'd1;
end
end
/* I/O mapped registers */
localparam IO_SEL_EXT = 2'd0;
localparam IO_SEL_STACK = 2'd1;
localparam IO_SEL_SREG = 2'd2;
reg [1:0] io_sel;
always @(posedge clk) begin
if(rst)
io_sel <= IO_SEL_EXT;
else begin
case(io_a)
6'b111101,
6'b111110: io_sel <= IO_SEL_STACK;
6'b111111: io_sel <= IO_SEL_SREG;
default: io_sel <= IO_SEL_EXT;
endcase
end
end
/* Register operations */
wire immediate = (pmem_d[14]
| (pmem_d[15:12] == 4'b0011)) /* CPI */
& (pmem_d[15:10] != 6'b111111) /* SBRC - SBRS */
& (pmem_d[15:10] != 6'b111110); /* BST - BLD */
reg lpm_en;
wire [4:0] Rd = lpm_en ? 5'd0 : {immediate | pmem_d[8], pmem_d[7:4]};
wire [4:0] Rr = {pmem_d[9], pmem_d[3:0]};
wire [7:0] K = {pmem_d[11:8], pmem_d[3:0]};
wire [2:0] b = pmem_d[2:0];
wire [11:0] Kl = pmem_d[11:0];
wire [6:0] Ks = pmem_d[9:3];
wire [1:0] Rd16 = pmem_d[5:4];
wire [5:0] K16 = {pmem_d[7:6], pmem_d[3:0]};
wire [5:0] q = {pmem_d[13], pmem_d[11:10], pmem_d[2:0]};
wire [7:0] GPR_Rd8 = GPR[Rd];
wire [7:0] GPR_Rr8 = GPR[Rr];
reg [7:0] GPR_Rd;
always @(*) begin
case(Rd)
default: GPR_Rd = GPR_Rd8;
5'd24: GPR_Rd = U[7:0];
5'd25: GPR_Rd = U[15:8];
5'd26: GPR_Rd = pX[7:0];
5'd27: GPR_Rd = pX[15:8];
5'd28: GPR_Rd = pY[7:0];
5'd29: GPR_Rd = pY[15:8];
5'd30: GPR_Rd = pZ[7:0];
5'd31: GPR_Rd = pZ[15:8];
endcase
end
reg [7:0] GPR_Rr;
always @(*) begin
case(Rr)
default: GPR_Rr = GPR_Rr8;
5'd24: GPR_Rr = U[7:0];
5'd25: GPR_Rr = U[15:8];
5'd26: GPR_Rr = pX[7:0];
5'd27: GPR_Rr = pX[15:8];
5'd28: GPR_Rr = pY[7:0];
5'd29: GPR_Rr = pY[15:8];
5'd30: GPR_Rr = pZ[7:0];
5'd31: GPR_Rr = pZ[15:8];
endcase
end
wire GPR_Rd_b = GPR_Rd[b];
reg [15:0] GPR_Rd16;
always @(*) begin
case(Rd16)
2'd0: GPR_Rd16 = U;
2'd1: GPR_Rd16 = pX;
2'd2: GPR_Rd16 = pY;
2'd3: GPR_Rd16 = pZ;
endcase
end
/* Memorize values to support 16-bit instructions */
reg regmem_ce;
reg [4:0] Rd_r;
reg [7:0] GPR_Rd_r;
always @(posedge clk) begin
if(regmem_ce)
Rd_r <= Rd; /* < control with regmem_ce */
GPR_Rd_r <= GPR_Rd; /* < always loaded */
end
/* PC */
reg [3:0] pc_sel;
localparam PC_SEL_NOP = 4'd0;
localparam PC_SEL_INC = 4'd1;
localparam PC_SEL_KL = 4'd2;
localparam PC_SEL_KS = 4'd3;
localparam PC_SEL_DMEML = 4'd4;
localparam PC_SEL_DMEMH = 4'd6;
localparam PC_SEL_DEC = 4'd7;
localparam PC_SEL_Z = 4'd8;
localparam PC_SEL_EX = 4'd9;
/* Exceptions */
reg [7:0] next_irq_ack;
always @(*) begin
next_irq_ack = 8'b0;
casex(irq)
8'bxxxx_xxx1: next_irq_ack = 8'b0000_0001;
8'bxxxx_xx10: next_irq_ack = 8'b0000_0010;
8'bxxxx_x100: next_irq_ack = 8'b0000_0100;
8'bxxxx_1000: next_irq_ack = 8'b0000_1000;
8'bxxx1_0000: next_irq_ack = 8'b0001_0000;
8'bxx10_0000: next_irq_ack = 8'b0010_0000;
8'bx100_0000: next_irq_ack = 8'b0100_0000;
8'b1000_0000: next_irq_ack = 8'b1000_0000;
endcase
end
reg irq_ack_en;
always @(posedge clk) begin
if(rst)
irq_ack <= 8'b0;
else begin
irq_ack <= 1'b0;
if(irq_ack_en)
irq_ack <= next_irq_ack;
end
end
/* Priority encoder */
reg [3:0] PC_ex;
always @(*) begin
PC_ex = 4'b0;
casex(irq)
8'bxxxx_xxx1: PC_ex = 4'h0;
8'bxxxx_xx10: PC_ex = 4'h1;
8'bxxxx_x100: PC_ex = 4'h2;
8'bxxxx_1000: PC_ex = 4'h3;
8'bxxx1_0000: PC_ex = 4'h4;
8'bxx10_0000: PC_ex = 4'h5;
8'bx100_0000: PC_ex = 4'h6;
8'b1000_0000: PC_ex = 4'h7;
endcase
end
/* AVR cores always execute at least one instruction after an IRET.
* Therefore, the I bit is only valid one clock after it has been set. */
reg I_r;
always @(posedge clk) begin
if(rst)
I_r <= 1'b0;
else
I_r <= I;
end
wire irq_asserted = |irq;
wire irq_request = I & I_r & irq_asserted;
always @(posedge clk) begin
if(rst) begin
`ifndef REGRESS
PC <= 0;
`endif
end else begin
case(pc_sel)
PC_SEL_NOP:;
PC_SEL_INC: PC <= PC + 1;
// !!! WARNING !!! replace with PC <= PC + {{pmem_width-12{Kl[11]}}, Kl}; if pmem_width>12
PC_SEL_KL: PC <= PC + Kl;
PC_SEL_KS: PC <= PC + {{pmem_width-7{Ks[6]}}, Ks};
PC_SEL_DMEML: PC[7:0] <= dmem_di;
PC_SEL_DMEMH: PC[pmem_width-1:8] <= dmem_di;
PC_SEL_DEC: PC <= PC - 1;
PC_SEL_Z: PC <= pZ - 1;
PC_SEL_EX: PC <= {{pmem_width-4{1'b0}}, PC_ex};
endcase
end
dbg_pc <= PC;
end
reg pmem_selz;
assign pmem_a = rst ?
`ifdef REGRESS
PC
`else
0
`endif
: (pmem_selz ? pZ[15:1] : PC + 1);
/* Load/store operations */
reg [3:0] dmem_sel;
localparam DMEM_SEL_UNDEFINED = 3'bxxx;
localparam DMEM_SEL_X = 4'd0;
localparam DMEM_SEL_XPLUS = 4'd1;
localparam DMEM_SEL_XMINUS = 4'd2;
localparam DMEM_SEL_YPLUS = 4'd3;
localparam DMEM_SEL_YMINUS = 4'd4;
localparam DMEM_SEL_YQ = 4'd5;
localparam DMEM_SEL_ZPLUS = 4'd6;
localparam DMEM_SEL_ZMINUS = 4'd7;
localparam DMEM_SEL_ZQ = 4'd8;
localparam DMEM_SEL_SP_R = 4'd9;
localparam DMEM_SEL_SP_PCL = 4'd10;
localparam DMEM_SEL_SP_PCH = 4'd11;
localparam DMEM_SEL_PMEM = 4'd12;
/* ALU */
reg normal_en;
reg lds_writeback;
wire [4:0] write_dest = lds_writeback ? Rd_r : Rd;
`define INIT_REGS
`ifdef INIT_REGS
integer i_rst_regf;
`endif
reg [7:0] R;
reg writeback;
reg update_svnz;
reg change_z;
reg [15:0] R16;
reg mode16;
reg _N;
reg _V;
reg _C;
reg I_clr;
reg I_set;
always @(posedge clk) begin
R = 8'hxx;
writeback = 1'b0;
update_svnz = 1'b0;
change_z = 1'b1;
R16 = 16'hxxxx;
mode16 = 1'b0;
if(rst) begin
`ifndef REGRESS
/*
* Not resetting the register file enables the use of more efficient
* distributed block RAM.
*/
`ifdef INIT_REGS
for(i_rst_regf=0;i_rst_regf<24;i_rst_regf=i_rst_regf+1)
GPR[i_rst_regf] <= 8'd0;
U <= 16'd0;
pX <= 16'd0;
pY <= 16'd0;
pZ <= 16'd0;
`endif
I <= 1'b0;
T <= 1'b0;
H <= 1'b0;
S <= 1'b0;
V <= 1'b0;
N <= 1'b0;
Z <= 1'b0;
C <= 1'b0;
_V = 1'b0;
_N = 1'b0;
_C = 1'b0;
`endif
end else begin
if(I_set)
I <= 1'b1;
if(normal_en) begin
writeback = 1'b1;
update_svnz = 1'b1;
casex(pmem_d)
16'b000x_11xx_xxxx_xxxx: begin
/* ADD - ADC */
{_C, R} = GPR_Rd + GPR_Rr + (pmem_d[12] & C);
C <= _C;
H <= (GPR_Rd[3] & GPR_Rr[3])|(GPR_Rr[3] & ~R[3])|(~R[3] & GPR_Rd[3]);
_V = (GPR_Rd[7] & GPR_Rr[7] & ~R[7])|(~GPR_Rd[7] & ~GPR_Rr[7] & R[7]);
end
16'b000x_10xx_xxxx_xxxx, /* subtract */
16'b000x_01xx_xxxx_xxxx: /* compare */ begin
/* SUB - SBC / CP - CPC */
{_C, R} = GPR_Rd - GPR_Rr - (~pmem_d[12] & C);
C <= _C;
H <= (~GPR_Rd[3] & GPR_Rr[3])|(GPR_Rr[3] & R[3])|(R[3] & ~GPR_Rd[3]);
_V = (GPR_Rd[7] & ~GPR_Rr[7] & ~R[7])|(~GPR_Rd[7] & GPR_Rr[7] & R[7]);
if(~pmem_d[12])
change_z = 1'b0;
writeback = pmem_d[11];
end
16'b010x_xxxx_xxxx_xxxx, /* subtract */
16'b0011_xxxx_xxxx_xxxx: /* compare */ begin
/* SUBI - SBCI / CPI */
{_C, R} = GPR_Rd - K - (~pmem_d[12] & C);
C <= _C;
H <= (~GPR_Rd[3] & K[3])|(K[3] & R[3])|(R[3] & ~GPR_Rd[3]);
_V = (GPR_Rd[7] & ~K[7] & ~R[7])|(~GPR_Rd[7] & K[7] & R[7]);
if(~pmem_d[12])
change_z = 1'b0;
writeback = pmem_d[14];
end
16'b0010_00xx_xxxx_xxxx: begin
/* AND */
R = GPR_Rd & GPR_Rr;
_V = 1'b0;
end
16'b0111_xxxx_xxxx_xxxx: begin
/* ANDI */
R = GPR_Rd & K;
_V = 1'b0;
end
16'b0010_10xx_xxxx_xxxx: begin
/* OR */
R = GPR_Rd | GPR_Rr;
_V = 1'b0;
end
16'b0110_xxxx_xxxx_xxxx: begin
/* ORI */
R = GPR_Rd | K;
_V = 1'b0;
end
16'b0010_01xx_xxxx_xxxx: begin
/* EOR */
R = GPR_Rd ^ GPR_Rr;
_V = 1'b0;
end
16'b1001_010x_xxxx_0000: begin
/* COM */
R = ~GPR_Rd;
_V = 1'b0;
C <= 1'b1;
end
16'b1001_010x_xxxx_0001: begin
/* NEG */
{_C, R} = 8'h00 - GPR_Rd;
C <= _C;
H <= R[3] | GPR_Rd[3];
_V = R == 8'h80;
end
16'b1001_010x_xxxx_0011: begin
/* INC */
R = GPR_Rd + 8'd1;
_V = R == 8'h80;
end
16'b1001_010x_xxxx_1010: begin
/* DEC */
R = GPR_Rd - 8'd1;
_V = R == 8'h7f;
end
16'b1001_010x_xxxx_011x: begin
/* LSR - ROR */
R = {pmem_d[0] & C, GPR_Rd[7:1]};
C <= GPR_Rd[0];
_V = R[7] ^ GPR_Rd[0];
end
16'b1001_010x_xxxx_0101: begin
/* ASR */
R = {GPR_Rd[7], GPR_Rd[7:1]};
C <= GPR_Rd[0];
_V = R[7] ^ GPR_Rd[0];
end
16'b1001_010x_xxxx_0010: begin
/* SWAP */
R = {GPR_Rd[3:0], GPR_Rd[7:4]};
update_svnz = 1'b0;
end
16'b1001_010x_xxxx_1000: begin
/* BSET - BCLR */
case(pmem_d[7:4])
4'b0000: C <= 1'b1;
4'b0001: Z <= 1'b1;
4'b0010: N <= 1'b1;
4'b0011: V <= 1'b1;
4'b0100: S <= 1'b1;
4'b0101: H <= 1'b1;
4'b0110: T <= 1'b1;
4'b0111: I <= 1'b1;
4'b1000: C <= 1'b0;
4'b1001: Z <= 1'b0;
4'b1010: N <= 1'b0;
4'b1011: V <= 1'b0;
4'b1100: S <= 1'b0;
4'b1101: H <= 1'b0;
4'b1110: T <= 1'b0;
4'b1111: I <= 1'b0;
endcase
update_svnz = 1'b0;
writeback = 1'b0;
end
16'b1001_011x_xxxx_xxxx: begin
mode16 = 1'b1;
if(pmem_d[8]) begin
/* SBIW */
{_C, R16} = GPR_Rd16 - K16;
C <= _C;
_V = GPR_Rd16[15] & ~R16[15];
end else begin
/* ADIW */
{_C, R16} = GPR_Rd16 + K16;
C <= _C;
_V = ~GPR_Rd16[15] & R16[15];
end
end
/* SBR and CBR are replaced with ORI and ANDI */
/* TST is replaced with AND */
/* CLR and SER are replaced with EOR and LDI */
16'b0010_11xx_xxxx_xxxx: begin
/* MOV */
R = GPR_Rr;
update_svnz = 1'b0;
end
16'b1110_xxxx_xxxx_xxxx: begin
/* LDI */
R = K;
update_svnz = 1'b0;
end
/* LSL is replaced with ADD */
/* ROL is replaced with ADC */
16'b1111_10xx_xxxx_0xxx: begin
if(pmem_d[9]) begin
/* BST */
T <= GPR_Rd_b;
writeback = 1'b0;
end else begin
/* BLD */
case(b)
3'd0: R = {GPR_Rd[7:1], T};
3'd1: R = {GPR_Rd[7:2], T, GPR_Rd[0]};
3'd2: R = {GPR_Rd[7:3], T, GPR_Rd[1:0]};
3'd3: R = {GPR_Rd[7:4], T, GPR_Rd[2:0]};
3'd4: R = {GPR_Rd[7:5], T, GPR_Rd[3:0]};
3'd5: R = {GPR_Rd[7:6], T, GPR_Rd[4:0]};
3'd6: R = {GPR_Rd[7], T, GPR_Rd[5:0]};
3'd7: R = {T, GPR_Rd[6:0]};
endcase
end
update_svnz = 1'b0;
end
/* SEC, CLC, SEN, CLN, SEZ, CLZ, SEI, CLI, SES, CLS, SEV, CLV, SET, CLT, SEH, CLH
* are replaced with BSET and BCLR */
16'b0000_0000_0000_0000: begin
/* NOP */
update_svnz = 1'b0;
writeback = 1'b0;
end
/* SLEEP is not implemented */
/* WDR is not implemented */
16'b1001_00xx_xxxx_1111, /* PUSH/POP */
16'b1001_00xx_xxxx_1100, /* X */
16'b1001_00xx_xxxx_1101, /* X+ */
16'b1001_00xx_xxxx_1110, /* -X */
16'b1001_00xx_xxxx_1001, /* Y+ */
16'b1001_00xx_xxxx_1010, /* -Y */
16'b10x0_xxxx_xxxx_1xxx, /* Y+q */
16'b1001_00xx_xxxx_0001, /* Z+ */
16'b1001_00xx_xxxx_0010, /* -Z */
16'b10x0_xxxx_xxxx_0xxx: /* Z+q */
begin
/* LD - POP (run from state WRITEBACK) */
R = dmem_di;
update_svnz = 1'b0;
end
16'b1011_0xxx_xxxx_xxxx: begin
/* IN (run from state WRITEBACK) */
case(io_sel)
IO_SEL_EXT: R = io_di;
IO_SEL_STACK: R = io_sp;
IO_SEL_SREG: R = {I, T, H, S, V, N, Z, C};
default: R = 8'hxx;
endcase
update_svnz = 1'b0;
end
endcase
end /* if(normal_en) */
if(lds_writeback) begin
R = dmem_di;
writeback = 1'b1;
end
if(lpm_en) begin
R = pZ[0] ? pmem_d[15:8] : pmem_d[7:0];
writeback = 1'b1;
end
if(update_svnz) begin
_N = mode16 ? R16[15] : R[7];
S <= _N ^ _V;
V <= _V;
N <= _N;
Z <= mode16 ? R16 == 16'h0000 : ((R == 8'h00) & (change_z|Z));
end
if(io_we & (io_a == 6'b111111))
{I, T, H, S, V, N, Z, C} <= io_do[7:0];
if(I_clr)
I <= 1'b0;
if(writeback) begin
if(mode16) begin
// $display("REG WRITE(16): %d < %d", Rd16, R16);
case(Rd16)
2'd0: U <= R16;
2'd1: pX <= R16;
2'd2: pY <= R16;
2'd3: pZ <= R16;
endcase
end else begin
// $display("REG WRITE: %d < %d", Rd, R);
case(write_dest)
default: GPR[write_dest] <= R;
5'd24: U[7:0] <= R;
5'd25: U[15:8] <= R;
5'd26: pX[7:0] <= R;
5'd27: pX[15:8] <= R;
5'd28: pY[7:0] <= R;
5'd29: pY[15:8] <= R;
5'd30: pZ[7:0] <= R;
5'd31: pZ[15:8] <= R;
endcase
end
end else begin /* if(writeback) */
case(dmem_sel)
DMEM_SEL_XPLUS: pX <= pX + 16'd1;
DMEM_SEL_XMINUS: pX <= pX - 16'd1;
DMEM_SEL_YPLUS: pY <= pY + 16'd1;
DMEM_SEL_YMINUS: pY <= pY - 16'd1;
DMEM_SEL_ZPLUS: pZ <= pZ + 16'd1;
DMEM_SEL_ZMINUS: pZ <= pZ - 16'd1;
default:;
endcase
end
end /* if(rst) ... else */
end
/* I/O port */
assign io_a = {pmem_d[10:9], pmem_d[3:0]};
assign io_do = GPR_Rd;
/* Data memory */
always @(*) begin
case(dmem_sel)
DMEM_SEL_X,
DMEM_SEL_XPLUS: dmem_a = pX;
DMEM_SEL_XMINUS: dmem_a = pX - 16'd1;
DMEM_SEL_YPLUS: dmem_a = pY;
DMEM_SEL_YMINUS: dmem_a = pY - 16'd1;
DMEM_SEL_YQ: dmem_a = pY + q;
DMEM_SEL_ZPLUS: dmem_a = pZ;
DMEM_SEL_ZMINUS: dmem_a = pZ - 16'd1;
DMEM_SEL_ZQ: dmem_a = pZ + q;
DMEM_SEL_SP_R,
DMEM_SEL_SP_PCL,
DMEM_SEL_SP_PCH: dmem_a = SP + pop;
DMEM_SEL_PMEM: dmem_a = pmem_d;
default: dmem_a = {dmem_width{1'bx}};
endcase
end
wire [pmem_width-1:0] PC_inc = PC + 1;
reg exception;
always @(*) begin
case(dmem_sel)
DMEM_SEL_X,
DMEM_SEL_XPLUS,
DMEM_SEL_XMINUS,
DMEM_SEL_YPLUS,
DMEM_SEL_YMINUS,
DMEM_SEL_YQ,
DMEM_SEL_ZPLUS,
DMEM_SEL_ZMINUS,
DMEM_SEL_ZQ,
DMEM_SEL_SP_R: dmem_do = GPR_Rd;
DMEM_SEL_SP_PCL: dmem_do = exception ? PC[7:0] : PC_inc[7:0];
DMEM_SEL_SP_PCH: dmem_do = exception ? PC[pmem_width-1:8] : PC_inc[pmem_width-1:8];
DMEM_SEL_PMEM: dmem_do = GPR_Rd_r;
default: dmem_do = 8'hxx;
endcase
end
/* Multi-cycle operation sequencer */
wire reg_equal = GPR_Rd == GPR_Rr;
reg sreg_read;
always @(*) begin
case(b)
3'd0: sreg_read = C;
3'd1: sreg_read = Z;
3'd2: sreg_read = N;
3'd3: sreg_read = V;
3'd4: sreg_read = S;
3'd5: sreg_read = H;
3'd6: sreg_read = T;
3'd7: sreg_read = I;
endcase
end
reg [4:0] state;
reg [4:0] next_state;
localparam NORMAL = 5'd0;
localparam RCALL = 5'd1;
localparam ICALL = 5'd2;
localparam STALL = 5'd3;
localparam RET1 = 5'd4;
localparam RET2 = 5'd5;
localparam RET3 = 5'd6;
localparam LPM = 5'd7;
localparam STS = 5'd8;
localparam LDS1 = 5'd9;
localparam LDS2 = 5'd10;
localparam SKIP = 5'd11;
localparam WRITEBACK = 5'd12;
localparam EXCEPTION = 5'd13;
localparam RETI1 = 5'd14;
localparam RETI2 = 5'd15;
localparam RETI3 = 5'd16;
localparam RETI4 = 5'd17;
always @(posedge clk) begin
if(rst)
state <= NORMAL;
else
state <= next_state;
end
always @(*) begin
next_state = state;
pmem_ce = rst;
pc_sel = PC_SEL_NOP;
normal_en = 1'b0;
lpm_en = 1'b0;
io_re = 1'b0;
io_we = 1'b0;
dmem_we = 1'b0;
dmem_sel = DMEM_SEL_UNDEFINED;
push = 1'b0;
pop = 1'b0;
exception = 1'b0;
irq_ack_en = 1'b0;
I_set = 1'b0;
I_clr = 1'b0;
pmem_selz = 1'b0;
regmem_ce = 1'b1;
lds_writeback = 1'b0;
case(state)
NORMAL: begin
if(irq_request) begin
dmem_sel = DMEM_SEL_SP_PCL;
dmem_we = 1'b1;
exception = 1'b1;
push = 1'b1;
irq_ack_en = 1'b1;
I_clr = 1'b1;
next_state = EXCEPTION;
end else begin
casex(pmem_d)
16'b1100_xxxx_xxxx_xxxx: begin
/* RJMP */
pc_sel = PC_SEL_KL;
next_state = STALL;
end
16'b1101_xxxx_xxxx_xxxx: begin
/* RCALL */
dmem_sel = DMEM_SEL_SP_PCL;
dmem_we = 1'b1;
push = 1'b1;
next_state = RCALL;
end
16'b0001_00xx_xxxx_xxxx: begin
/* CPSE */
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
if(reg_equal)
next_state = SKIP;
end
16'b1111_11xx_xxxx_0xxx: begin
/* SBRC - SBRS */
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
if(GPR_Rd_b == pmem_d[9])
next_state = SKIP;
end
/* SBIC, SBIS, SBI, CBI are not implemented */
16'b1111_0xxx_xxxx_xxxx: begin
/* BRBS - BRBC */
pmem_ce = 1'b1;
if(sreg_read ^ pmem_d[10]) begin
pc_sel = PC_SEL_KS;
next_state = STALL;
end else
pc_sel = PC_SEL_INC;
end
/* BREQ, BRNE, BRCS, BRCC, BRSH, BRLO, BRMI, BRPL, BRGE, BRLT,
* BRHS, BRHC, BRTS, BRTC, BRVS, BRVC, BRIE, BRID are replaced
* with BRBS/BRBC */
16'b1001_00xx_xxxx_1100, /* X */
16'b1001_00xx_xxxx_1101, /* X+ */
16'b1001_00xx_xxxx_1110, /* -X */
16'b1001_00xx_xxxx_1001, /* Y+ */
16'b1001_00xx_xxxx_1010, /* -Y */
16'b10x0_xxxx_xxxx_1xxx, /* Y+q */
16'b1001_00xx_xxxx_0001, /* Z+ */
16'b1001_00xx_xxxx_0010, /* -Z */
16'b10x0_xxxx_xxxx_0xxx: /* Z+q */
begin
casex({pmem_d[12], pmem_d[3:0]})
5'b1_1100: dmem_sel = DMEM_SEL_X;
5'b1_1101: dmem_sel = DMEM_SEL_XPLUS;
5'b1_1110: dmem_sel = DMEM_SEL_XMINUS;
5'b1_1001: dmem_sel = DMEM_SEL_YPLUS;
5'b1_1010: dmem_sel = DMEM_SEL_YMINUS;
5'b0_1xxx: dmem_sel = DMEM_SEL_YQ;
5'b1_0001: dmem_sel = DMEM_SEL_ZPLUS;
5'b1_0010: dmem_sel = DMEM_SEL_ZMINUS;
5'b0_0xxx: dmem_sel = DMEM_SEL_ZQ;
endcase
if(pmem_d[9]) begin
/* ST */
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
dmem_we = 1'b1;
end else begin
/* LD */
next_state = WRITEBACK;
end
end
16'b1011_0xxx_xxxx_xxxx: begin
/* IN */
io_re = 1'b1;
next_state = WRITEBACK;
end
16'b1011_1xxx_xxxx_xxxx: begin
/* OUT */
io_we = 1'b1;
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
end
16'b1001_00xx_xxxx_1111: begin
if(pmem_d[9]) begin
/* PUSH */
push = 1'b1;
dmem_sel = DMEM_SEL_SP_R;
dmem_we = 1'b1;
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
end else begin
/* POP */
pop = 1'b1;
dmem_sel = DMEM_SEL_SP_R;
next_state = WRITEBACK;
end
end
16'b1001_00xx_xxxx_0000: begin
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
if(pmem_d[9])
/* STS */
next_state = STS;
else
/* LDS */
next_state = LDS1;
end
16'b1001_0101_000x_1000: begin
/* RET / RETI */
dmem_sel = DMEM_SEL_SP_PCH;
pop = 1'b1;
if(pmem_d[4] == 1'b0)
next_state = RET1;
else
next_state = RETI1;
end
16'b1001_0101_1100_1000: begin
/* LPM */
pmem_selz = 1'b1;
pmem_ce = 1'b1;
next_state = LPM;
end
16'b1001_0100_0000_1001: begin
/* IJMP */
pc_sel = PC_SEL_Z;
next_state = STALL;
end
16'b1001_0101_0000_1001: begin
/* ICALL */
dmem_sel = DMEM_SEL_SP_PCL;
dmem_we = 1'b1;
push = 1'b1;
next_state = ICALL;
end
default: begin
pc_sel = PC_SEL_INC;
normal_en = 1'b1;
pmem_ce = 1'b1;
end
endcase
end
end
RCALL: begin
dmem_sel = DMEM_SEL_SP_PCH;
dmem_we = 1'b1;
push = 1'b1;
pc_sel = PC_SEL_KL;
next_state = STALL;
end
EXCEPTION: begin
dmem_sel = DMEM_SEL_SP_PCH;
dmem_we = 1'b1;
exception = 1'b1;
push = 1'b1;
pc_sel = PC_SEL_EX;
next_state = STALL;
end
ICALL: begin
dmem_sel = DMEM_SEL_SP_PCH;
dmem_we = 1'b1;
push = 1'b1;
pc_sel = PC_SEL_Z;
next_state = STALL;
end
RET1: begin
pc_sel = PC_SEL_DMEMH;
dmem_sel = DMEM_SEL_SP_PCL;
pop = 1'b1;
next_state = RET2;
end
RET2: begin
pc_sel = PC_SEL_DMEML;
next_state = RET3;
end
RET3: begin
pc_sel = PC_SEL_DEC;
next_state = STALL;
end
RETI1: begin
pc_sel = PC_SEL_DMEMH;
dmem_sel = DMEM_SEL_SP_PCL;
pop = 1'b1;
next_state = RETI2;
end
RETI2: begin
pc_sel = PC_SEL_DMEML;
next_state = RETI3;
end
RETI3: begin
pc_sel = PC_SEL_DEC;
next_state = RETI4;
end
RETI4: begin
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
I_set = 1'b1;
next_state = NORMAL;
end
LPM: begin
lpm_en = 1'b1;
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
next_state = NORMAL;
end
STS: begin
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
dmem_sel = DMEM_SEL_PMEM;
dmem_we = 1'b1;
next_state = NORMAL;
end
LDS1: begin
dmem_sel = DMEM_SEL_PMEM;
regmem_ce = 1'b0;
next_state = LDS2;
end
LDS2: begin
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
lds_writeback = 1'b1;
next_state = NORMAL;
end
SKIP: begin
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
/* test for STS and LDS */
if((pmem_d[15:10] == 6'b100100) & (pmem_d[3:0] == 4'h0))
next_state = STALL; /* 2-word instruction, skip the second word as well */
else
next_state = NORMAL; /* 1-word instruction */
end
STALL: begin
pc_sel = PC_SEL_INC;
pmem_ce = 1'b1;
next_state = NORMAL;
end
WRITEBACK: begin
pmem_ce = 1'b1;
pc_sel = PC_SEL_INC;
normal_en = 1'b1;
next_state = NORMAL;
end
endcase
end
`ifdef REGRESS
integer i;
integer cycles;
always @(posedge clk) begin
if(~rst & (state == NORMAL) & (cycles != 0)) begin
$display("DUMP REGISTERS");
for(i=0;i<24;i=i+1)
$display("%x", GPR[i]);
$display("%x", U[7:0]);
$display("%x", U[15:8]);
$display("%x", pX[7:0]);
$display("%x", pX[15:8]);
$display("%x", pY[7:0]);
$display("%x", pY[15:8]);
$display("%x", pZ[7:0]);
$display("%x", pZ[15:8]);
$display("%x", {I, T, H, S, V, N, Z, C});
$display("%x", SP[15:8]);
$display("%x", SP[7:0]);
$display("%x", PC[pmem_width-1:7]);
$display("%x", {PC[6:0], 1'b0});
tb_regress.dump;
$finish;
end
if(rst)
cycles = 0;
else
cycles = cycles + 1;
end
reg [7:0] SPR[0:12];
initial begin
$readmemh("gpr.rom", GPR);
$readmemh("spr.rom", SPR);
U = {SPR[1], SPR[0]};
pX = {SPR[3], SPR[2]};
pY = {SPR[5], SPR[4]};
pZ = {SPR[7], SPR[6]};
{I, T, H, S, V, N, Z, C} = SPR[8];
SP = {SPR[9], SPR[10]};
PC = {SPR[11], SPR[12]}/2;
end
`endif
endmodule
module testbench;
parameter pmem_width = 11;
parameter dmem_width = 13;
// navre inputs
reg clk;
reg rst;
reg [15:0] pmem_d;
reg [7:0] dmem_di;
reg [7:0] io_di;
reg [7:0] irq;
// navre outputs
wire pmem_ce;
wire [pmem_width-1:0] pmem_a;
wire dmem_we;
wire [dmem_width-1:0] dmem_a;
wire [7:0] dmem_do;
wire io_re;
wire io_we;
wire [5:0] io_a;
wire [7:0] io_do;
wire [7:0] irq_ack;
wire [pmem_width-1:0] dbg_pc;
softusb_navre #(
pmem_width,
dmem_width
) UUT (
clk,
rst,
pmem_ce,
pmem_a,
pmem_d,
dmem_we,
dmem_a,
dmem_di,
dmem_do,
io_re,
io_we,
io_a,
io_do,
io_di,
irq,
irq_ack,
dbg_pc
);
integer cycles;
initial begin
clk <= 1;
rst <= 1;
cycles = 0;
while (cycles < 8) begin
#50; clk <= ~clk;
cycles = cycles + 1;
#50; clk <= ~clk;
end
rst <= #20 0;
forever begin
#50; clk <= ~clk;
cycles = cycles + 1;
#50; clk <= ~clk;
if (cycles == 10000) begin
$display("Reached limit of 10000 cpu cycles.");
$finish;
end
end
end
reg [15:0] addr;
reg [15:0] pmem [2**pmem_width-1:0];
reg [ 7:0] dmem [2**dmem_width-1:0];
integer output_idx;
reg [7:0] output_buf [1023:0];
event output_eof;
integer i;
initial begin
for (i=0; i < 2**pmem_width; i = i+1) begin
pmem[i] = 0;
end
for (i=0; i < 2**dmem_width; i = i+1) begin
dmem[i] = 0;
end
`include "sieve.v"
output_idx = 0;
end
always @(posedge clk) begin
if (rst) begin
pmem_d <= 0;
irq <= 0;
end else if (pmem_ce) begin
addr = pmem_a * 2;
$display("+LOG+ %t PR @%x %x", $time, addr, pmem[pmem_a]);
pmem_d <= pmem[pmem_a];
end
if (dmem_we) begin
addr = dmem_a;
$display("+LOG+ %t DW @%x %x", $time, addr, dmem_do);
dmem[dmem_a] <= dmem_do;
end
if (io_we && io_a == 42) begin
addr = io_a;
$display("+LOG+ %t IO @%x %x <---", $time, addr, io_do);
if (io_do == 0) begin
-> output_eof;
end else begin
output_buf[output_idx] = io_do;
output_idx = output_idx + 1;
end
end
dmem_di <= dmem[dmem_a];
io_di <= 0;
end
always @(output_eof) begin
#1001;
$display("Got EOF marker on IO port.");
for (i = 0; i < output_idx; i = i + 1) begin
$display("+OUT+ %t %d", $time, output_buf[i]);
end
$finish;
end
initial begin
// $dumpfile("bench.vcd");
// $dumpvars(0, testbench);
end
endmodule
#!/bin/sh
set -ex
avr-gcc -Wall -Os -fno-inline -mmcu=avr2 -o sieve.elf sieve.c
avr-objcopy -O ihex sieve.elf sieve.ihex
python ihex2vlog.py < sieve.ihex > sieve.v
avr-objdump -d sieve.elf
rm -f sieve.elf sieve.ihex
#!/usr/bin/python
from __future__ import division
from __future__ import print_function
import re
ihex_pattern = re.compile(':([0-9a-fA-F]{2})([0-9a-fA-F]{4})([0-9a-fA-F]{2})([0-9a-fA-F]*)([0-9a-fA-F]{2})')
while True:
m = ihex_pattern.match(raw_input())
if m:
count = int(m.group(1), 16)
address = int(m.group(2), 16)
rectype = int(m.group(3), 16)
data = [ int(m.group(4)[2*i:2*i+2], 16) for i in range(len(m.group(4))//2) ]
checksum = int(m.group(5), 16)
assert (checksum + count + address + address >> 8 + rectype + reduce(lambda x, y: x+y, data, 0)) & 0xff == 0
if rectype == 0x00:
for i in range(len(data)//2):
waddr = address//2 + i;
print("pmem[%4d] = 16'h%04x;" % (waddr, data[2*i] | data[2*i+1] << 8))
elif rectype == 0x01:
break
elif rectype == 0x03:
pass
else:
assert 0
// A simple Sieve of Eratosthenes
#include <stdint.h>
#include <stdbool.h>
#ifndef AVR
# include <stdio.h>
#else
# include <avr/io.h>
#endif
#define BITMAP_SIZE 24
#define OUTPORT 42
static uint8_t bitmap[BITMAP_SIZE/8];
static void bitmap_set(uint8_t idx)
{
bitmap[idx/8] |= 1 << (idx % 8);
}
static bool bitmap_get(uint8_t idx)
{
return (bitmap[idx/8] & (1 << (idx % 8))) != 0;
}
static void output(uint8_t val)
{
#ifndef AVR
printf("%d\n", val);
#else
_SFR_IO8(OUTPORT) = val;
#endif
}
int main()
{
uint8_t i, j, k;
output(2);
for (i = 0; i < BITMAP_SIZE; i++) {
if (bitmap_get(i))
continue;
output(3+2*i);
for (j = 2*(3+2*i);; j += 3+2*i) {
if (j%2 == 0)
continue;
k = (j-3)/2;
if (k >= BITMAP_SIZE)
break;
bitmap_set(k);
}
}
output(0);
return 0;
}
pmem[ 0] = 16'hc00c;
pmem[ 1] = 16'hc01b;
pmem[ 2] = 16'hc01a;
pmem[ 3] = 16'hc019;
pmem[ 4] = 16'hc018;
pmem[ 5] = 16'hc017;
pmem[ 6] = 16'hc016;
pmem[ 7] = 16'hc015;
pmem[ 8] = 16'hc014;
pmem[ 9] = 16'hc013;
pmem[ 10] = 16'hc012;
pmem[ 11] = 16'hc011;
pmem[ 12] = 16'hc010;
pmem[ 13] = 16'h2411;
pmem[ 14] = 16'hbe1f;
pmem[ 15] = 16'he5cf;
pmem[ 16] = 16'he0d2;
pmem[ 17] = 16'hbfde;
pmem[ 18] = 16'hbfcd;
pmem[ 19] = 16'he010;
pmem[ 20] = 16'he6a0;
pmem[ 21] = 16'he0b0;
pmem[ 22] = 16'hc001;
pmem[ 23] = 16'h921d;
pmem[ 24] = 16'h36a3;
pmem[ 25] = 16'h07b1;
pmem[ 26] = 16'hf7e1;
pmem[ 27] = 16'hd028;
pmem[ 28] = 16'hc059;
pmem[ 29] = 16'hcfe2;
pmem[ 30] = 16'h2fe8;
pmem[ 31] = 16'h95e6;
pmem[ 32] = 16'h95e6;
pmem[ 33] = 16'h95e6;
pmem[ 34] = 16'he0f0;
pmem[ 35] = 16'h5ae0;
pmem[ 36] = 16'h4fff;
pmem[ 37] = 16'h7087;
pmem[ 38] = 16'he021;
pmem[ 39] = 16'he030;
pmem[ 40] = 16'hc001;
pmem[ 41] = 16'h0f22;
pmem[ 42] = 16'h958a;
pmem[ 43] = 16'hf7ea;
pmem[ 44] = 16'h8180;
pmem[ 45] = 16'h2b82;
pmem[ 46] = 16'h8380;
pmem[ 47] = 16'h9508;
pmem[ 48] = 16'h2fe8;
pmem[ 49] = 16'h95e6;
pmem[ 50] = 16'h95e6;
pmem[ 51] = 16'h95e6;
pmem[ 52] = 16'he0f0;
pmem[ 53] = 16'h5ae0;
pmem[ 54] = 16'h4fff;
pmem[ 55] = 16'h8120;
pmem[ 56] = 16'he030;
pmem[ 57] = 16'h7087;
pmem[ 58] = 16'hc002;
pmem[ 59] = 16'h9535;
pmem[ 60] = 16'h9527;
pmem[ 61] = 16'h958a;
pmem[ 62] = 16'hf7e2;
pmem[ 63] = 16'h2f82;
pmem[ 64] = 16'h7081;
pmem[ 65] = 16'h9508;
pmem[ 66] = 16'hbd8a;
pmem[ 67] = 16'h9508;
pmem[ 68] = 16'h931f;
pmem[ 69] = 16'h93cf;
pmem[ 70] = 16'h93df;
pmem[ 71] = 16'he082;
pmem[ 72] = 16'hdff9;
pmem[ 73] = 16'he0c3;
pmem[ 74] = 16'he0d0;
pmem[ 75] = 16'h2f8d;
pmem[ 76] = 16'hdfe3;
pmem[ 77] = 16'h1181;
pmem[ 78] = 16'hc01b;
pmem[ 79] = 16'h2f8c;
pmem[ 80] = 16'hdff1;
pmem[ 81] = 16'h2f1c;
pmem[ 82] = 16'h0f11;
pmem[ 83] = 16'hff10;
pmem[ 84] = 16'hc013;
pmem[ 85] = 16'h2f41;
pmem[ 86] = 16'he050;
pmem[ 87] = 16'h2f24;
pmem[ 88] = 16'h2f35;
pmem[ 89] = 16'h5023;
pmem[ 90] = 16'h0931;
pmem[ 91] = 16'hff37;
pmem[ 92] = 16'hc004;
pmem[ 93] = 16'h2f24;
pmem[ 94] = 16'h2f35;
pmem[ 95] = 16'h5022;
pmem[ 96] = 16'h0931;
pmem[ 97] = 16'h2f82;
pmem[ 98] = 16'h2f93;
pmem[ 99] = 16'h9595;
pmem[ 100] = 16'h9587;
pmem[ 101] = 16'h3188;
pmem[ 102] = 16'hf418;
pmem[ 103] = 16'hdfb6;
pmem[ 104] = 16'h0f1c;
pmem[ 105] = 16'hcfe9;
pmem[ 106] = 16'h5fdf;
pmem[ 107] = 16'h5fce;
pmem[ 108] = 16'h31d8;
pmem[ 109] = 16'hf6e9;
pmem[ 110] = 16'he080;
pmem[ 111] = 16'hdfd2;
pmem[ 112] = 16'he080;
pmem[ 113] = 16'he090;
pmem[ 114] = 16'h91df;
pmem[ 115] = 16'h91cf;
pmem[ 116] = 16'h911f;
pmem[ 117] = 16'h9508;
pmem[ 118] = 16'h94f8;
pmem[ 119] = 16'hcfff;
#!/bin/bash
set -ex
source $1/config
mkdir $1/work_$2
cd $1/work_$2
iverilog_cmd="iverilog -o sim -s testbench -I../rtl -I../sim"
rtl_files=""
for fn in $RTL; do
rtl_files="$rtl_files ../rtl/$fn"
done
case "$2" in
sim)
iverilog_cmd="$iverilog_cmd $rtl_files"
;;
cmos)
yosys -ql synthlog.txt -p "synth -top $TOP; abc -g cmos4; write_verilog synth.v" $rtl_files
iverilog_cmd="$iverilog_cmd synth.v"
;;
ice40)
yosys -ql synthlog.txt -p "synth_ice40 -top $TOP; write_verilog synth.v" $rtl_files
iverilog_cmd="$iverilog_cmd synth.v $(yosys-config --datdir/ice40/cells_sim.v)"
;;
*)
exit 1
;;
esac
for fn in $SIM; do
iverilog_cmd="$iverilog_cmd ../sim/$fn"
done
$iverilog_cmd
vvp -N sim | pv -l > output.txt
if [ "$2" != "sim" ]; then
if cmp output.txt ../work_sim/output.txt; then
echo pass > ../../${1}_${2}.status
else
echo fail > ../../${1}_${2}.status
fi
fi
Test cases for Yosys+Verific (mostly using SymbiYosys)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment