/*
|
/*
|
* Milkymist VJ SoC
|
* Milkymist VJ SoC
|
* Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
|
* Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
|
*
|
*
|
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
* the Free Software Foundation, version 3 of the License.
|
* the Free Software Foundation, version 3 of the License.
|
*
|
*
|
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
*
|
*
|
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
*/
|
*/
|
|
|
module softusb_navre #(
|
module softusb_navre #(
|
parameter pmem_width = 11, /* < in 16-bit instructions */
|
parameter pmem_width = 11, /* < in 16-bit instructions */
|
parameter dmem_width = 13 /* < in bytes */
|
parameter dmem_width = 13 /* < in bytes */
|
) (
|
) (
|
input clk,
|
input clk,
|
input rst,
|
input rst,
|
|
|
output reg pmem_ce,
|
output reg pmem_ce,
|
output [pmem_width-1:0] pmem_a,
|
output [pmem_width-1:0] pmem_a,
|
input [15:0] pmem_d,
|
input [15:0] pmem_d,
|
|
|
output reg dmem_we,
|
output reg dmem_we,
|
output reg [dmem_width-1:0] dmem_a,
|
output reg [dmem_width-1:0] dmem_a,
|
input [7:0] dmem_di,
|
input [7:0] dmem_di,
|
output reg [7:0] dmem_do,
|
output reg [7:0] dmem_do,
|
|
|
output reg io_re,
|
output reg io_re,
|
output reg io_we,
|
output reg io_we,
|
output [5:0] io_a,
|
output [5:0] io_a,
|
output [7:0] io_do,
|
output [7:0] io_do,
|
input [7:0] io_di
|
input [7:0] io_di
|
);
|
);
|
|
|
/* Register file */
|
/* Register file */
|
reg [pmem_width-1:0] PC;
|
reg [pmem_width-1:0] PC;
|
reg [7:0] GPR[0:23];
|
reg [7:0] GPR[0:23];
|
reg [15:0] U; /* < R24-R25 */
|
reg [15:0] U; /* < R24-R25 */
|
reg [15:0] pX; /* < R26-R27 */
|
reg [15:0] pX; /* < R26-R27 */
|
reg [15:0] pY; /* < R28-R29 */
|
reg [15:0] pY; /* < R28-R29 */
|
reg [15:0] pZ; /* < R30-R31 */
|
reg [15:0] pZ; /* < R30-R31 */
|
reg T, H, S, V, N, Z, C;
|
reg T, H, S, V, N, Z, C;
|
|
|
/* Stack */
|
/* Stack */
|
reg [7:0] io_sp;
|
reg [7:0] io_sp;
|
reg [15:0] SP;
|
reg [15:0] SP;
|
reg push;
|
reg push;
|
reg pop;
|
reg pop;
|
always @(posedge clk) begin
|
always @(posedge clk) begin
|
if(rst) begin
|
if(rst) begin
|
io_sp <= 8'd0;
|
io_sp <= 8'd0;
|
|
`ifndef REGRESS
|
SP <= 16'd0;
|
SP <= 16'd0;
|
|
`endif
|
end else begin
|
end else begin
|
io_sp <= io_a[0] ? SP[7:0] : SP[15:8];
|
io_sp <= io_a[0] ? SP[7:0] : SP[15:8];
|
if((io_a == 6'b111101) | (io_a == 6'b111110)) begin
|
if((io_a == 6'b111101) | (io_a == 6'b111110)) begin
|
if(io_we) begin
|
if(io_we) begin
|
if(io_a[0])
|
if(io_a[0])
|
SP[7:0] <= io_do;
|
SP[7:0] <= io_do;
|
else
|
else
|
SP[15:8] <= io_do;
|
SP[15:8] <= io_do;
|
end
|
end
|
end
|
end
|
if(push)
|
if(push)
|
SP <= SP - 16'd1;
|
SP <= SP - 16'd1;
|
if(pop)
|
if(pop)
|
SP <= SP + 16'd1;
|
SP <= SP + 16'd1;
|
end
|
end
|
end
|
end
|
|
|
/* I/O mapped registers */
|
/* I/O mapped registers */
|
|
|
parameter IO_SEL_EXT = 2'd0;
|
parameter IO_SEL_EXT = 2'd0;
|
parameter IO_SEL_STACK = 2'd1;
|
parameter IO_SEL_STACK = 2'd1;
|
parameter IO_SEL_SREG = 2'd2;
|
parameter IO_SEL_SREG = 2'd2;
|
|
|
reg [1:0] io_sel;
|
reg [1:0] io_sel;
|
always @(posedge clk) begin
|
always @(posedge clk) begin
|
if(rst)
|
if(rst)
|
io_sel <= IO_SEL_EXT;
|
io_sel <= IO_SEL_EXT;
|
else begin
|
else begin
|
case(io_a)
|
case(io_a)
|
6'b111101,
|
6'b111101,
|
6'b111110: io_sel <= IO_SEL_STACK;
|
6'b111110: io_sel <= IO_SEL_STACK;
|
6'b111111: io_sel <= IO_SEL_SREG;
|
6'b111111: io_sel <= IO_SEL_SREG;
|
default: io_sel <= IO_SEL_EXT;
|
default: io_sel <= IO_SEL_EXT;
|
endcase
|
endcase
|
end
|
end
|
end
|
end
|
|
|
/* Register operations */
|
/* Register operations */
|
wire immediate = (pmem_d[14]
|
wire immediate = (pmem_d[14]
|
| (pmem_d[15:12] == 4'b0011)) /* CPI */
|
| (pmem_d[15:12] == 4'b0011)) /* CPI */
|
& (pmem_d[15:10] != 6'b111111); /* SBRC - SBRS */
|
& (pmem_d[15:10] != 6'b111111) /* SBRC - SBRS */
|
|
& (pmem_d[15:10] != 6'b111110); /* BST - BLD */
|
reg lpm_en;
|
reg lpm_en;
|
wire [4:0] Rd = lpm_en ? 5'd0 : {immediate | pmem_d[8], pmem_d[7:4]};
|
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 [4:0] Rr = {pmem_d[9], pmem_d[3:0]};
|
wire [7:0] K = {pmem_d[11:8], 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 [2:0] b = pmem_d[2:0];
|
wire [11:0] Kl = pmem_d[11:0];
|
wire [11:0] Kl = pmem_d[11:0];
|
wire [6:0] Ks = pmem_d[9:3];
|
wire [6:0] Ks = pmem_d[9:3];
|
wire [1:0] Rd16 = pmem_d[5:4];
|
wire [1:0] Rd16 = pmem_d[5:4];
|
wire [5:0] K16 = {pmem_d[7:6], pmem_d[3:0]};
|
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 [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_Rd8 = GPR[Rd];
|
wire [7:0] GPR_Rr8 = GPR[Rr];
|
wire [7:0] GPR_Rr8 = GPR[Rr];
|
reg [7:0] GPR_Rd;
|
reg [7:0] GPR_Rd;
|
always @(*) begin
|
always @(*) begin
|
case(Rd)
|
case(Rd)
|
default: GPR_Rd = GPR_Rd8;
|
default: GPR_Rd = GPR_Rd8;
|
5'd24: GPR_Rd = U[7:0];
|
5'd24: GPR_Rd = U[7:0];
|
5'd25: GPR_Rd = U[15:8];
|
5'd25: GPR_Rd = U[15:8];
|
5'd26: GPR_Rd = pX[7:0];
|
5'd26: GPR_Rd = pX[7:0];
|
5'd27: GPR_Rd = pX[15:8];
|
5'd27: GPR_Rd = pX[15:8];
|
5'd28: GPR_Rd = pY[7:0];
|
5'd28: GPR_Rd = pY[7:0];
|
5'd29: GPR_Rd = pY[15:8];
|
5'd29: GPR_Rd = pY[15:8];
|
5'd30: GPR_Rd = pZ[7:0];
|
5'd30: GPR_Rd = pZ[7:0];
|
5'd31: GPR_Rd = pZ[15:8];
|
5'd31: GPR_Rd = pZ[15:8];
|
endcase
|
endcase
|
end
|
end
|
reg [7:0] GPR_Rr;
|
reg [7:0] GPR_Rr;
|
always @(*) begin
|
always @(*) begin
|
case(Rr)
|
case(Rr)
|
default: GPR_Rr = GPR_Rr8;
|
default: GPR_Rr = GPR_Rr8;
|
5'd24: GPR_Rr = U[7:0];
|
5'd24: GPR_Rr = U[7:0];
|
5'd25: GPR_Rr = U[15:8];
|
5'd25: GPR_Rr = U[15:8];
|
5'd26: GPR_Rr = pX[7:0];
|
5'd26: GPR_Rr = pX[7:0];
|
5'd27: GPR_Rr = pX[15:8];
|
5'd27: GPR_Rr = pX[15:8];
|
5'd28: GPR_Rr = pY[7:0];
|
5'd28: GPR_Rr = pY[7:0];
|
5'd29: GPR_Rr = pY[15:8];
|
5'd29: GPR_Rr = pY[15:8];
|
5'd30: GPR_Rr = pZ[7:0];
|
5'd30: GPR_Rr = pZ[7:0];
|
5'd31: GPR_Rr = pZ[15:8];
|
5'd31: GPR_Rr = pZ[15:8];
|
endcase
|
endcase
|
end
|
end
|
wire GPR_Rd_b = GPR_Rd[b];
|
wire GPR_Rd_b = GPR_Rd[b];
|
|
|
reg [15:0] GPR_Rd16;
|
reg [15:0] GPR_Rd16;
|
always @(*) begin
|
always @(*) begin
|
case(Rd16)
|
case(Rd16)
|
2'd0: GPR_Rd16 = U;
|
2'd0: GPR_Rd16 = U;
|
2'd1: GPR_Rd16 = pX;
|
2'd1: GPR_Rd16 = pX;
|
2'd2: GPR_Rd16 = pY;
|
2'd2: GPR_Rd16 = pY;
|
2'd3: GPR_Rd16 = pZ;
|
2'd3: GPR_Rd16 = pZ;
|
endcase
|
endcase
|
end
|
end
|
|
|
/* Memorize values to support 16-bit instructions */
|
/* Memorize values to support 16-bit instructions */
|
reg regmem_ce;
|
reg regmem_ce;
|
|
|
reg [4:0] Rd_r;
|
reg [4:0] Rd_r;
|
reg [7:0] GPR_Rd_r;
|
reg [7:0] GPR_Rd_r;
|
always @(posedge clk) begin
|
always @(posedge clk) begin
|
if(regmem_ce)
|
if(regmem_ce)
|
Rd_r <= Rd; /* < control with regmem_ce */
|
Rd_r <= Rd; /* < control with regmem_ce */
|
GPR_Rd_r <= GPR_Rd; /* < always loaded */
|
GPR_Rd_r <= GPR_Rd; /* < always loaded */
|
end
|
end
|
|
|
/* PC */
|
/* PC */
|
|
|
reg [3:0] pc_sel;
|
reg [3:0] pc_sel;
|
|
|
parameter PC_SEL_NOP = 4'd0;
|
parameter PC_SEL_NOP = 4'd0;
|
parameter PC_SEL_INC = 4'd1;
|
parameter PC_SEL_INC = 4'd1;
|
parameter PC_SEL_KL = 4'd2;
|
parameter PC_SEL_KL = 4'd2;
|
parameter PC_SEL_KS = 4'd3;
|
parameter PC_SEL_KS = 4'd3;
|
parameter PC_SEL_DMEML = 4'd4;
|
parameter PC_SEL_DMEML = 4'd4;
|
parameter PC_SEL_DMEMH = 4'd6;
|
parameter PC_SEL_DMEMH = 4'd6;
|
parameter PC_SEL_DEC = 4'd7;
|
parameter PC_SEL_DEC = 4'd7;
|
parameter PC_SEL_Z = 4'd7;
|
parameter PC_SEL_Z = 4'd8;
|
|
|
always @(posedge clk) begin
|
always @(posedge clk) begin
|
if(rst) begin
|
if(rst) begin
|
|
`ifndef REGRESS
|
PC <= 0;
|
PC <= 0;
|
|
`endif
|
end else begin
|
end else begin
|
case(pc_sel)
|
case(pc_sel)
|
PC_SEL_NOP:;
|
PC_SEL_NOP:;
|
PC_SEL_INC: PC <= PC + 1;
|
PC_SEL_INC: PC <= PC + 1;
|
// !!! WARNING !!! replace with PC <= PC + {{pmem_width-12{Kl[11]}}, Kl}; if pmem_width>12
|
// !!! WARNING !!! replace with PC <= PC + {{pmem_width-12{Kl[11]}}, Kl}; if pmem_width>12
|
PC_SEL_KL: PC <= PC + Kl;
|
PC_SEL_KL: PC <= PC + Kl;
|
PC_SEL_KS: PC <= PC + {{pmem_width-7{Ks[6]}}, Ks};
|
PC_SEL_KS: PC <= PC + {{pmem_width-7{Ks[6]}}, Ks};
|
PC_SEL_DMEML: PC[7:0] <= dmem_di;
|
PC_SEL_DMEML: PC[7:0] <= dmem_di;
|
PC_SEL_DMEMH: PC[pmem_width-1:8] <= dmem_di;
|
PC_SEL_DMEMH: PC[pmem_width-1:8] <= dmem_di;
|
PC_SEL_DEC: PC <= PC - 1;
|
PC_SEL_DEC: PC <= PC - 1;
|
PC_SEL_Z: PC <= pZ - 1;
|
PC_SEL_Z: PC <= pZ - 1;
|
endcase
|
endcase
|
end
|
end
|
end
|
end
|
reg pmem_selz;
|
reg pmem_selz;
|
assign pmem_a = rst ? 0 : (pmem_selz ? pZ[15:1] : PC + 1);
|
assign pmem_a = rst ?
|
|
`ifdef REGRESS
|
|
PC
|
|
`else
|
|
0
|
|
`endif
|
|
: (pmem_selz ? pZ[15:1] : PC + 1);
|
|
|
/* Load/store operations */
|
/* Load/store operations */
|
reg [3:0] dmem_sel;
|
reg [3:0] dmem_sel;
|
|
|
parameter DMEM_SEL_UNDEFINED = 3'bxxx;
|
parameter DMEM_SEL_UNDEFINED = 3'bxxx;
|
parameter DMEM_SEL_X = 4'd0;
|
parameter DMEM_SEL_X = 4'd0;
|
parameter DMEM_SEL_XPLUS = 4'd1;
|
parameter DMEM_SEL_XPLUS = 4'd1;
|
parameter DMEM_SEL_XMINUS = 4'd2;
|
parameter DMEM_SEL_XMINUS = 4'd2;
|
parameter DMEM_SEL_YPLUS = 4'd3;
|
parameter DMEM_SEL_YPLUS = 4'd3;
|
parameter DMEM_SEL_YMINUS = 4'd4;
|
parameter DMEM_SEL_YMINUS = 4'd4;
|
parameter DMEM_SEL_YQ = 4'd5;
|
parameter DMEM_SEL_YQ = 4'd5;
|
parameter DMEM_SEL_ZPLUS = 4'd6;
|
parameter DMEM_SEL_ZPLUS = 4'd6;
|
parameter DMEM_SEL_ZMINUS = 4'd7;
|
parameter DMEM_SEL_ZMINUS = 4'd7;
|
parameter DMEM_SEL_ZQ = 4'd8;
|
parameter DMEM_SEL_ZQ = 4'd8;
|
parameter DMEM_SEL_SP_R = 4'd9;
|
parameter DMEM_SEL_SP_R = 4'd9;
|
parameter DMEM_SEL_SP_PCL = 4'd10;
|
parameter DMEM_SEL_SP_PCL = 4'd10;
|
parameter DMEM_SEL_SP_PCH = 4'd11;
|
parameter DMEM_SEL_SP_PCH = 4'd11;
|
parameter DMEM_SEL_PMEM = 4'd12;
|
parameter DMEM_SEL_PMEM = 4'd12;
|
|
|
/* ALU */
|
/* ALU */
|
|
|
reg normal_en;
|
reg normal_en;
|
reg lds_writeback;
|
reg lds_writeback;
|
|
|
wire [4:0] write_dest = lds_writeback ? Rd_r : Rd;
|
wire [4:0] write_dest = lds_writeback ? Rd_r : Rd;
|
|
|
// synthesis translate_off
|
// synthesis translate_off
|
integer i_rst_regf;
|
integer i_rst_regf;
|
// synthesis translate_on
|
// synthesis translate_on
|
reg [7:0] R;
|
reg [7:0] R;
|
reg writeback;
|
reg writeback;
|
reg update_nsz;
|
reg update_nsz;
|
|
reg change_z;
|
reg [15:0] R16;
|
reg [15:0] R16;
|
reg mode16;
|
reg mode16;
|
always @(posedge clk) begin
|
always @(posedge clk) begin
|
R = 8'hxx;
|
R = 8'hxx;
|
writeback = 1'b0;
|
writeback = 1'b0;
|
update_nsz = 1'b0;
|
update_nsz = 1'b0;
|
|
change_z = 1'b1;
|
R16 = 16'hxxxx;
|
R16 = 16'hxxxx;
|
mode16 = 1'b0;
|
mode16 = 1'b0;
|
if(rst) begin
|
if(rst) begin
|
|
`ifndef REGRESS
|
/*
|
/*
|
* Not resetting the register file enables the use of more efficient
|
* Not resetting the register file enables the use of more efficient
|
* distributed block RAM.
|
* distributed block RAM.
|
*/
|
*/
|
// synthesis translate_off
|
// synthesis translate_off
|
for(i_rst_regf=0;i_rst_regf<24;i_rst_regf=i_rst_regf+1)
|
for(i_rst_regf=0;i_rst_regf<24;i_rst_regf=i_rst_regf+1)
|
GPR[i_rst_regf] = 8'd0;
|
GPR[i_rst_regf] = 8'd0;
|
U = 16'd0;
|
U = 16'd0;
|
pX = 16'd0;
|
pX = 16'd0;
|
pY = 16'd0;
|
pY = 16'd0;
|
pZ = 16'd0;
|
pZ = 16'd0;
|
// synthesis translate_on
|
// synthesis translate_on
|
T = 1'b0;
|
T = 1'b0;
|
H = 1'b0;
|
H = 1'b0;
|
S = 1'b0;
|
S = 1'b0;
|
V = 1'b0;
|
V = 1'b0;
|
N = 1'b0;
|
N = 1'b0;
|
Z = 1'b0;
|
Z = 1'b0;
|
C = 1'b0;
|
C = 1'b0;
|
|
`endif
|
end else begin
|
end else begin
|
if(normal_en) begin
|
if(normal_en) begin
|
writeback = 1'b1;
|
writeback = 1'b1;
|
update_nsz = 1'b1;
|
update_nsz = 1'b1;
|
casex(pmem_d)
|
casex(pmem_d)
|
16'b000x_11xx_xxxx_xxxx: begin
|
16'b000x_11xx_xxxx_xxxx: begin
|
/* ADD - ADC */
|
/* ADD - ADC */
|
{C, R} = GPR_Rd + GPR_Rr + (pmem_d[12] & C);
|
{C, R} = GPR_Rd + GPR_Rr + (pmem_d[12] & C);
|
H = (GPR_Rd[3] & GPR_Rr[3])|(GPR_Rr[3] & ~R[3])|(~R[3] & GPR_Rd[3]);
|
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]);
|
V = (GPR_Rd[7] & GPR_Rr[7] & ~R[7])|(~GPR_Rd[7] & ~GPR_Rr[7] & R[7]);
|
end
|
end
|
16'b000x_10xx_xxxx_xxxx, /* subtract */
|
16'b000x_10xx_xxxx_xxxx, /* subtract */
|
16'b000x_01xx_xxxx_xxxx: /* compare */ begin
|
16'b000x_01xx_xxxx_xxxx: /* compare */ begin
|
/* SUB - SBC / CP - CPC */
|
/* SUB - SBC / CP - CPC */
|
{C, R} = GPR_Rd - GPR_Rr - (~pmem_d[12] & C);
|
{C, R} = GPR_Rd - GPR_Rr - (~pmem_d[12] & C);
|
H = (~GPR_Rd[3] & GPR_Rr[3])|(GPR_Rr[3] & R[3])|(R[3] & ~GPR_Rd[3]);
|
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]);
|
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];
|
writeback = pmem_d[11];
|
end
|
end
|
16'b010x_xxxx_xxxx_xxxx, /* subtract */
|
16'b010x_xxxx_xxxx_xxxx, /* subtract */
|
16'b0011_xxxx_xxxx_xxxx: /* compare */ begin
|
16'b0011_xxxx_xxxx_xxxx: /* compare */ begin
|
/* SUBI - SBCI / CPI */
|
/* SUBI - SBCI / CPI */
|
{C, R} = GPR_Rd - K - (~pmem_d[12] & C);
|
{C, R} = GPR_Rd - K - (~pmem_d[12] & C);
|
H = (~GPR_Rd[3] & K[3])|(K[3] & R[3])|(R[3] & ~GPR_Rd[3]);
|
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]);
|
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];
|
writeback = pmem_d[14];
|
end
|
end
|
16'b0010_00xx_xxxx_xxxx: begin
|
16'b0010_00xx_xxxx_xxxx: begin
|
/* AND */
|
/* AND */
|
R = GPR_Rd & GPR_Rr;
|
R = GPR_Rd & GPR_Rr;
|
V = 1'b0;
|
V = 1'b0;
|
end
|
end
|
16'b0111_xxxx_xxxx_xxxx: begin
|
16'b0111_xxxx_xxxx_xxxx: begin
|
/* ANDI */
|
/* ANDI */
|
R = GPR_Rd & K;
|
R = GPR_Rd & K;
|
V = 1'b0;
|
V = 1'b0;
|
end
|
end
|
16'b0010_10xx_xxxx_xxxx: begin
|
16'b0010_10xx_xxxx_xxxx: begin
|
/* OR */
|
/* OR */
|
R = GPR_Rd | GPR_Rr;
|
R = GPR_Rd | GPR_Rr;
|
V = 1'b0;
|
V = 1'b0;
|
end
|
end
|
16'b0110_xxxx_xxxx_xxxx: begin
|
16'b0110_xxxx_xxxx_xxxx: begin
|
/* ORI */
|
/* ORI */
|
R = GPR_Rd | K;
|
R = GPR_Rd | K;
|
V = 1'b0;
|
V = 1'b0;
|
end
|
end
|
16'b0010_01xx_xxxx_xxxx: begin
|
16'b0010_01xx_xxxx_xxxx: begin
|
/* EOR */
|
/* EOR */
|
R = GPR_Rd ^ GPR_Rr;
|
R = GPR_Rd ^ GPR_Rr;
|
V = 1'b0;
|
V = 1'b0;
|
end
|
end
|
16'b1001_010x_xxxx_0000: begin
|
16'b1001_010x_xxxx_0000: begin
|
/* COM */
|
/* COM */
|
R = ~GPR_Rd;
|
R = ~GPR_Rd;
|
V = 1'b0;
|
V = 1'b0;
|
C = 1'b1;
|
C = 1'b1;
|
end
|
end
|
16'b1001_010x_xxxx_0001: begin
|
16'b1001_010x_xxxx_0001: begin
|
/* NEG */
|
/* NEG */
|
{C, R} = 8'h00 - GPR_Rd;
|
{C, R} = 8'h00 - GPR_Rd;
|
H = R[3] | GPR_Rd[3];
|
H = R[3] | GPR_Rd[3];
|
V = R == 8'h80;
|
V = R == 8'h80;
|
end
|
end
|
16'b1001_010x_xxxx_0011: begin
|
16'b1001_010x_xxxx_0011: begin
|
/* INC */
|
/* INC */
|
R = GPR_Rd + 8'd1;
|
R = GPR_Rd + 8'd1;
|
V = R == 8'h80;
|
V = R == 8'h80;
|
end
|
end
|
16'b1001_010x_xxxx_1010: begin
|
16'b1001_010x_xxxx_1010: begin
|
/* DEC */
|
/* DEC */
|
R = GPR_Rd - 8'd1;
|
R = GPR_Rd - 8'd1;
|
V = R == 8'h7f;
|
V = R == 8'h7f;
|
end
|
end
|
16'b1001_010x_xxxx_011x: begin
|
16'b1001_010x_xxxx_011x: begin
|
/* LSR - ROR */
|
/* LSR - ROR */
|
R = {pmem_d[0] & C, GPR_Rd[7:1]};
|
R = {pmem_d[0] & C, GPR_Rd[7:1]};
|
C = GPR_Rd[0];
|
C = GPR_Rd[0];
|
V = R[7] ^ GPR_Rd[0];
|
V = R[7] ^ GPR_Rd[0];
|
end
|
end
|
16'b1001_010x_xxxx_0101: begin
|
16'b1001_010x_xxxx_0101: begin
|
/* ASR */
|
/* ASR */
|
R = {GPR_Rd[7], GPR_Rd[7:1]};
|
R = {GPR_Rd[7], GPR_Rd[7:1]};
|
C = GPR_Rd[0];
|
C = GPR_Rd[0];
|
V = R[7] ^ GPR_Rd[0];
|
V = R[7] ^ GPR_Rd[0];
|
end
|
end
|
16'b1001_010x_xxxx_0010: begin
|
16'b1001_010x_xxxx_0010: begin
|
/* SWAP */
|
/* SWAP */
|
R = {GPR_Rd[3:0], GPR_Rd[7:4]};
|
R = {GPR_Rd[3:0], GPR_Rd[7:4]};
|
update_nsz = 1'b0;
|
update_nsz = 1'b0;
|
end
|
end
|
16'b1001_010x_xxxx_1000: begin
|
16'b1001_010x_xxxx_1000: begin
|
/* BSET - BCLR */
|
/* BSET - BCLR */
|
case(pmem_d[7:4])
|
case(pmem_d[7:4])
|
4'b0000: C = 1'b1;
|
4'b0000: C = 1'b1;
|
4'b0001: Z = 1'b1;
|
4'b0001: Z = 1'b1;
|
4'b0010: N = 1'b1;
|
4'b0010: N = 1'b1;
|
4'b0011: V = 1'b1;
|
4'b0011: V = 1'b1;
|
4'b0100: S = 1'b1;
|
4'b0100: S = 1'b1;
|
4'b0101: H = 1'b1;
|
4'b0101: H = 1'b1;
|
4'b0110: T = 1'b1;
|
4'b0110: T = 1'b1;
|
4'b1000: C = 1'b0;
|
4'b1000: C = 1'b0;
|
4'b1001: Z = 1'b0;
|
4'b1001: Z = 1'b0;
|
4'b1010: N = 1'b0;
|
4'b1010: N = 1'b0;
|
4'b1011: V = 1'b0;
|
4'b1011: V = 1'b0;
|
4'b1100: S = 1'b0;
|
4'b1100: S = 1'b0;
|
4'b1101: H = 1'b0;
|
4'b1101: H = 1'b0;
|
4'b1110: T = 1'b0;
|
4'b1110: T = 1'b0;
|
endcase
|
endcase
|
update_nsz = 1'b0;
|
update_nsz = 1'b0;
|
writeback = 1'b0;
|
writeback = 1'b0;
|
end
|
end
|
16'b1001_011x_xxxx_xxxx: begin
|
16'b1001_011x_xxxx_xxxx: begin
|
mode16 = 1'b1;
|
mode16 = 1'b1;
|
if(pmem_d[8]) begin
|
if(pmem_d[8]) begin
|
/* SBIW */
|
/* SBIW */
|
{C, R16} = GPR_Rd16 - K16;
|
{C, R16} = GPR_Rd16 - K16;
|
V = GPR_Rd16[15] & ~R16[15];
|
V = GPR_Rd16[15] & ~R16[15];
|
end else begin
|
end else begin
|
/* ADIW */
|
/* ADIW */
|
{C, R16} = GPR_Rd16 + K16;
|
{C, R16} = GPR_Rd16 + K16;
|
V = ~GPR_Rd16[15] & R16[15];
|
V = ~GPR_Rd16[15] & R16[15];
|
end
|
end
|
end
|
end
|
/* SBR and CBR are replaced with ORI and ANDI */
|
/* SBR and CBR are replaced with ORI and ANDI */
|
/* TST is replaced with AND */
|
/* TST is replaced with AND */
|
/* CLR and SER are replaced with EOR and LDI */
|
/* CLR and SER are replaced with EOR and LDI */
|
16'b0010_11xx_xxxx_xxxx: begin
|
16'b0010_11xx_xxxx_xxxx: begin
|
/* MOV */
|
/* MOV */
|
R = GPR_Rr;
|
R = GPR_Rr;
|
update_nsz = 1'b0;
|
update_nsz = 1'b0;
|
end
|
end
|
16'b1110_xxxx_xxxx_xxxx: begin
|
16'b1110_xxxx_xxxx_xxxx: begin
|
/* LDI */
|
/* LDI */
|
R = K;
|
R = K;
|
update_nsz = 1'b0;
|
update_nsz = 1'b0;
|
end
|
end
|
/* LSL is replaced with ADD */
|
/* LSL is replaced with ADD */
|
/* ROL is replaced with ADC */
|
/* ROL is replaced with ADC */
|
16'b1111_10xx_xxxx_xxxx: begin
|
16'b1111_10xx_xxxx_0xxx: begin
|
if(pmem_d[9]) begin
|
if(pmem_d[9]) begin
|
/* BST */
|
/* BST */
|
T = GPR_Rd_b;
|
T = GPR_Rd_b;
|
writeback = 1'b0;
|
writeback = 1'b0;
|
end else begin
|
end else begin
|
/* BLD */
|
/* BLD */
|
case(b)
|
case(b)
|
3'd0: R = {GPR_Rd[7:1], T};
|
3'd0: R = {GPR_Rd[7:1], T};
|
3'd1: R = {GPR_Rd[7:2], T, GPR_Rd[0]};
|
3'd1: R = {GPR_Rd[7:2], T, GPR_Rd[0]};
|
3'd2: R = {GPR_Rd[7:3], T, GPR_Rd[1: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'd3: R = {GPR_Rd[7:4], T, GPR_Rd[2:0]};
|
3'd4: R = {GPR_Rd[7:5], T, GPR_Rd[3: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'd5: R = {GPR_Rd[7:6], T, GPR_Rd[4:0]};
|
3'd6: R = {GPR_Rd[7], T, GPR_Rd[5:0]};
|
3'd6: R = {GPR_Rd[7], T, GPR_Rd[5:0]};
|
3'd7: R = {T, GPR_Rd[6:0]};
|
3'd7: R = {T, GPR_Rd[6:0]};
|
endcase
|
endcase
|
end
|
end
|
update_nsz = 1'b0;
|
update_nsz = 1'b0;
|
end
|
end
|
/* SEC, CLC, SEN, CLN, SEZ, CLZ, SEI, CLI, SES, CLS, SEV, CLV, SET, CLT, SEH, CLH
|
/* SEC, CLC, SEN, CLN, SEZ, CLZ, SEI, CLI, SES, CLS, SEV, CLV, SET, CLT, SEH, CLH
|
* are replaced with BSET and BCLR */
|
* are replaced with BSET and BCLR */
|
16'b0000_0000_0000_0000: begin
|
16'b0000_0000_0000_0000: begin
|
/* NOP */
|
/* NOP */
|
update_nsz = 1'b0;
|
update_nsz = 1'b0;
|
writeback = 1'b0;
|
writeback = 1'b0;
|
end
|
end
|
/* SLEEP is not implemented */
|
/* SLEEP is not implemented */
|
/* WDR is not implemented */
|
/* WDR is not implemented */
|
16'b1001_00xx_xxxx_1111, /* PUSH/POP */
|
16'b1001_00xx_xxxx_1111, /* PUSH/POP */
|
16'b1001_00xx_xxxx_1100, /* X */
|
16'b1001_00xx_xxxx_1100, /* X */
|
16'b1001_00xx_xxxx_1101, /* X+ */
|
16'b1001_00xx_xxxx_1101, /* X+ */
|
16'b1001_00xx_xxxx_1110, /* -X */
|
16'b1001_00xx_xxxx_1110, /* -X */
|
16'b1001_00xx_xxxx_1001, /* Y+ */
|
16'b1001_00xx_xxxx_1001, /* Y+ */
|
16'b1001_00xx_xxxx_1010, /* -Y */
|
16'b1001_00xx_xxxx_1010, /* -Y */
|
16'b10x0_xxxx_xxxx_1xxx, /* Y+q */
|
16'b10x0_xxxx_xxxx_1xxx, /* Y+q */
|
16'b1001_00xx_xxxx_0001, /* Z+ */
|
16'b1001_00xx_xxxx_0001, /* Z+ */
|
16'b1001_00xx_xxxx_0010, /* -Z */
|
16'b1001_00xx_xxxx_0010, /* -Z */
|
16'b10x0_xxxx_xxxx_0xxx: /* Z+q */
|
16'b10x0_xxxx_xxxx_0xxx: /* Z+q */
|
begin
|
begin
|
/* LD - POP (run from state WRITEBACK) */
|
/* LD - POP (run from state WRITEBACK) */
|
R = dmem_di;
|
R = dmem_di;
|
update_nsz = 1'b0;
|
update_nsz = 1'b0;
|
end
|
end
|
16'b1011_0xxx_xxxx_xxxx: begin
|
16'b1011_0xxx_xxxx_xxxx: begin
|
/* IN (run from state WRITEBACK) */
|
/* IN (run from state WRITEBACK) */
|
case(io_sel)
|
case(io_sel)
|
IO_SEL_EXT: R = io_di;
|
IO_SEL_EXT: R = io_di;
|
IO_SEL_STACK: R = io_sp;
|
IO_SEL_STACK: R = io_sp;
|
IO_SEL_SREG: R = {1'b0, T, H, S, V, N, Z, C};
|
IO_SEL_SREG: R = {1'b0, T, H, S, V, N, Z, C};
|
default: R = 8'hxx;
|
default: R = 8'hxx;
|
endcase
|
endcase
|
update_nsz = 1'b0;
|
update_nsz = 1'b0;
|
end
|
end
|
endcase
|
endcase
|
end /* if(normal_en) */
|
end /* if(normal_en) */
|
if(lds_writeback) begin
|
if(lds_writeback) begin
|
R = dmem_di;
|
R = dmem_di;
|
writeback = 1'b1;
|
writeback = 1'b1;
|
end
|
end
|
if(lpm_en) begin
|
if(lpm_en) begin
|
R = pZ[0] ? pmem_d[15:8] : pmem_d[7:0];
|
R = pZ[0] ? pmem_d[15:8] : pmem_d[7:0];
|
writeback = 1'b1;
|
writeback = 1'b1;
|
end
|
end
|
if(update_nsz) begin
|
if(update_nsz) begin
|
N = mode16 ? R16[15] : R[7];
|
N = mode16 ? R16[15] : R[7];
|
S = N ^ V;
|
S = N ^ V;
|
Z = mode16 ? R16 == 16'h0000 : R == 8'h00;
|
Z = mode16 ? R16 == 16'h0000 : ((R == 8'h00) & (change_z|Z));
|
end
|
end
|
if(io_we & (io_a == 6'b111111))
|
if(io_we & (io_a == 6'b111111))
|
{T, H, S, V, N, Z, C} = io_do[6:0];
|
{T, H, S, V, N, Z, C} = io_do[6:0];
|
if(writeback) begin
|
if(writeback) begin
|
if(mode16) begin
|
if(mode16) begin
|
// synthesis translate_off
|
// synthesis translate_off
|
//$display("REG WRITE(16): %d < %d", Rd16, R16);
|
//$display("REG WRITE(16): %d < %d", Rd16, R16);
|
// synthesis translate_on
|
// synthesis translate_on
|
case(Rd16)
|
case(Rd16)
|
2'd0: U = R16;
|
2'd0: U = R16;
|
2'd1: pX = R16;
|
2'd1: pX = R16;
|
2'd2: pY = R16;
|
2'd2: pY = R16;
|
2'd3: pZ = R16;
|
2'd3: pZ = R16;
|
endcase
|
endcase
|
end else begin
|
end else begin
|
// synthesis translate_off
|
// synthesis translate_off
|
//$display("REG WRITE: %d < %d", Rd, R);
|
//$display("REG WRITE: %d < %d", Rd, R);
|
// synthesis translate_on
|
// synthesis translate_on
|
case(write_dest)
|
case(write_dest)
|
default: GPR[write_dest] = R;
|
default: GPR[write_dest] = R;
|
5'd24: U[7:0] = R;
|
5'd24: U[7:0] = R;
|
5'd25: U[15:8] = R;
|
5'd25: U[15:8] = R;
|
5'd26: pX[7:0] = R;
|
5'd26: pX[7:0] = R;
|
5'd27: pX[15:8] = R;
|
5'd27: pX[15:8] = R;
|
5'd28: pY[7:0] = R;
|
5'd28: pY[7:0] = R;
|
5'd29: pY[15:8] = R;
|
5'd29: pY[15:8] = R;
|
5'd30: pZ[7:0] = R;
|
5'd30: pZ[7:0] = R;
|
5'd31: pZ[15:8] = R;
|
5'd31: pZ[15:8] = R;
|
endcase
|
endcase
|
end
|
end
|
end else begin /* if(writeback) */
|
end else begin /* if(writeback) */
|
case(dmem_sel)
|
case(dmem_sel)
|
DMEM_SEL_XPLUS: pX = pX + 16'd1;
|
DMEM_SEL_XPLUS: pX = pX + 16'd1;
|
DMEM_SEL_XMINUS: pX = pX - 16'd1;
|
DMEM_SEL_XMINUS: pX = pX - 16'd1;
|
DMEM_SEL_YPLUS: pY = pY + 16'd1;
|
DMEM_SEL_YPLUS: pY = pY + 16'd1;
|
DMEM_SEL_YMINUS: pY = pY - 16'd1;
|
DMEM_SEL_YMINUS: pY = pY - 16'd1;
|
DMEM_SEL_ZPLUS: pZ = pZ + 16'd1;
|
DMEM_SEL_ZPLUS: pZ = pZ + 16'd1;
|
DMEM_SEL_ZMINUS: pZ = pZ - 16'd1;
|
DMEM_SEL_ZMINUS: pZ = pZ - 16'd1;
|
default:;
|
default:;
|
endcase
|
endcase
|
end
|
end
|
end /* if(sys_rst) ... else */
|
end /* if(rst) ... else */
|
end
|
end
|
|
|
/* I/O port */
|
/* I/O port */
|
assign io_a = {pmem_d[10:9], pmem_d[3:0]};
|
assign io_a = {pmem_d[10:9], pmem_d[3:0]};
|
assign io_do = GPR_Rd;
|
assign io_do = GPR_Rd;
|
|
|
/* Data memory */
|
/* Data memory */
|
always @(*) begin
|
always @(*) begin
|
case(dmem_sel)
|
case(dmem_sel)
|
DMEM_SEL_X,
|
DMEM_SEL_X,
|
DMEM_SEL_XPLUS: dmem_a = pX;
|
DMEM_SEL_XPLUS: dmem_a = pX;
|
DMEM_SEL_XMINUS: dmem_a = pX - 16'd1;
|
DMEM_SEL_XMINUS: dmem_a = pX - 16'd1;
|
DMEM_SEL_YPLUS: dmem_a = pY;
|
DMEM_SEL_YPLUS: dmem_a = pY;
|
DMEM_SEL_YMINUS: dmem_a = pY - 16'd1;
|
DMEM_SEL_YMINUS: dmem_a = pY - 16'd1;
|
DMEM_SEL_YQ: dmem_a = pY + q;
|
DMEM_SEL_YQ: dmem_a = pY + q;
|
DMEM_SEL_ZPLUS: dmem_a = pZ;
|
DMEM_SEL_ZPLUS: dmem_a = pZ;
|
DMEM_SEL_ZMINUS: dmem_a = pZ - 16'd1;
|
DMEM_SEL_ZMINUS: dmem_a = pZ - 16'd1;
|
DMEM_SEL_ZQ: dmem_a = pZ + q;
|
DMEM_SEL_ZQ: dmem_a = pZ + q;
|
DMEM_SEL_SP_R,
|
DMEM_SEL_SP_R,
|
DMEM_SEL_SP_PCL,
|
DMEM_SEL_SP_PCL,
|
DMEM_SEL_SP_PCH: dmem_a = SP + pop;
|
DMEM_SEL_SP_PCH: dmem_a = SP + pop;
|
DMEM_SEL_PMEM: dmem_a = pmem_d;
|
DMEM_SEL_PMEM: dmem_a = pmem_d;
|
default: dmem_a = {dmem_width{1'bx}};
|
default: dmem_a = {dmem_width{1'bx}};
|
endcase
|
endcase
|
end
|
end
|
|
|
wire [pmem_width-1:0] PC_inc = PC + 1;
|
wire [pmem_width-1:0] PC_inc = PC + 1;
|
always @(*) begin
|
always @(*) begin
|
case(dmem_sel)
|
case(dmem_sel)
|
DMEM_SEL_X,
|
DMEM_SEL_X,
|
|
DMEM_SEL_XPLUS,
|
DMEM_SEL_XMINUS,
|
DMEM_SEL_XMINUS,
|
|
DMEM_SEL_YPLUS,
|
DMEM_SEL_YMINUS,
|
DMEM_SEL_YMINUS,
|
DMEM_SEL_YQ,
|
DMEM_SEL_YQ,
|
|
DMEM_SEL_ZPLUS,
|
DMEM_SEL_ZMINUS,
|
DMEM_SEL_ZMINUS,
|
DMEM_SEL_ZQ,
|
DMEM_SEL_ZQ,
|
DMEM_SEL_SP_R: dmem_do = GPR_Rd;
|
DMEM_SEL_SP_R: dmem_do = GPR_Rd;
|
DMEM_SEL_SP_PCL: dmem_do = PC_inc[7:0];
|
DMEM_SEL_SP_PCL: dmem_do = PC_inc[7:0];
|
DMEM_SEL_SP_PCH: dmem_do = PC_inc[pmem_width-1:8];
|
DMEM_SEL_SP_PCH: dmem_do = PC_inc[pmem_width-1:8];
|
DMEM_SEL_PMEM: dmem_do = GPR_Rd_r;
|
DMEM_SEL_PMEM: dmem_do = GPR_Rd_r;
|
default: dmem_do = 8'hxx;
|
default: dmem_do = 8'hxx;
|
endcase
|
endcase
|
end
|
end
|
|
|
/* Multi-cycle operation sequencer */
|
/* Multi-cycle operation sequencer */
|
|
|
wire reg_equal = GPR_Rd == GPR_Rr;
|
wire reg_equal = GPR_Rd == GPR_Rr;
|
|
|
reg sreg_read;
|
reg sreg_read;
|
always @(*) begin
|
always @(*) begin
|
case(b)
|
case(b)
|
3'd0: sreg_read = C;
|
3'd0: sreg_read = C;
|
3'd1: sreg_read = Z;
|
3'd1: sreg_read = Z;
|
3'd2: sreg_read = N;
|
3'd2: sreg_read = N;
|
3'd3: sreg_read = V;
|
3'd3: sreg_read = V;
|
3'd4: sreg_read = S;
|
3'd4: sreg_read = S;
|
3'd5: sreg_read = H;
|
3'd5: sreg_read = H;
|
3'd6: sreg_read = T;
|
3'd6: sreg_read = T;
|
3'd7: sreg_read = 1'b0;
|
3'd7: sreg_read = 1'b0;
|
endcase
|
endcase
|
end
|
end
|
|
|
reg [3:0] state;
|
reg [3:0] state;
|
reg [3:0] next_state;
|
reg [3:0] next_state;
|
|
|
parameter NORMAL = 4'd0;
|
parameter NORMAL = 4'd0;
|
parameter RCALL = 4'd1;
|
parameter RCALL = 4'd1;
|
parameter ICALL = 4'd2;
|
parameter ICALL = 4'd2;
|
parameter STALL = 4'd3;
|
parameter STALL = 4'd3;
|
parameter RET1 = 4'd4;
|
parameter RET1 = 4'd4;
|
parameter RET2 = 4'd5;
|
parameter RET2 = 4'd5;
|
parameter RET3 = 4'd6;
|
parameter RET3 = 4'd6;
|
parameter LPM = 4'd7;
|
parameter LPM = 4'd7;
|
parameter STS = 4'd8;
|
parameter STS = 4'd8;
|
parameter LDS1 = 4'd9;
|
parameter LDS1 = 4'd9;
|
parameter LDS2 = 4'd10;
|
parameter LDS2 = 4'd10;
|
parameter SKIP = 4'd11;
|
parameter SKIP = 4'd11;
|
parameter WRITEBACK = 4'd12;
|
parameter WRITEBACK = 4'd12;
|
|
|
always @(posedge clk) begin
|
always @(posedge clk) begin
|
if(rst)
|
if(rst)
|
state <= NORMAL;
|
state <= NORMAL;
|
else
|
else
|
state <= next_state;
|
state <= next_state;
|
end
|
end
|
|
|
always @(*) begin
|
always @(*) begin
|
next_state = state;
|
next_state = state;
|
|
|
pmem_ce = rst;
|
pmem_ce = rst;
|
|
|
pc_sel = PC_SEL_NOP;
|
pc_sel = PC_SEL_NOP;
|
normal_en = 1'b0;
|
normal_en = 1'b0;
|
lpm_en = 1'b0;
|
lpm_en = 1'b0;
|
|
|
io_re = 1'b0;
|
io_re = 1'b0;
|
io_we = 1'b0;
|
io_we = 1'b0;
|
|
|
dmem_we = 1'b0;
|
dmem_we = 1'b0;
|
dmem_sel = DMEM_SEL_UNDEFINED;
|
dmem_sel = DMEM_SEL_UNDEFINED;
|
|
|
push = 1'b0;
|
push = 1'b0;
|
pop = 1'b0;
|
pop = 1'b0;
|
|
|
pmem_selz = 1'b0;
|
pmem_selz = 1'b0;
|
|
|
regmem_ce = 1'b1;
|
regmem_ce = 1'b1;
|
lds_writeback = 1'b0;
|
lds_writeback = 1'b0;
|
|
|
case(state)
|
case(state)
|
NORMAL: begin
|
NORMAL: begin
|
casex(pmem_d)
|
casex(pmem_d)
|
16'b1100_xxxx_xxxx_xxxx: begin
|
16'b1100_xxxx_xxxx_xxxx: begin
|
/* RJMP */
|
/* RJMP */
|
pc_sel = PC_SEL_KL;
|
pc_sel = PC_SEL_KL;
|
next_state = STALL;
|
next_state = STALL;
|
end
|
end
|
16'b1101_xxxx_xxxx_xxxx: begin
|
16'b1101_xxxx_xxxx_xxxx: begin
|
/* RCALL */
|
/* RCALL */
|
/* TODO: in which order should we push the bytes? */
|
/* TODO: in which order should we push the bytes? */
|
dmem_sel = DMEM_SEL_SP_PCL;
|
dmem_sel = DMEM_SEL_SP_PCL;
|
dmem_we = 1'b1;
|
dmem_we = 1'b1;
|
push = 1'b1;
|
push = 1'b1;
|
next_state = RCALL;
|
next_state = RCALL;
|
end
|
end
|
16'b0001_00xx_xxxx_xxxx: begin
|
16'b0001_00xx_xxxx_xxxx: begin
|
/* CPSE */
|
/* CPSE */
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
if(reg_equal)
|
if(reg_equal)
|
next_state = SKIP;
|
next_state = SKIP;
|
end
|
end
|
16'b1111_11xx_xxxx_xxxx: begin
|
16'b1111_11xx_xxxx_0xxx: begin
|
/* SBRC - SBRS */
|
/* SBRC - SBRS */
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
if(GPR_Rd_b == pmem_d[9])
|
if(GPR_Rd_b == pmem_d[9])
|
next_state = SKIP;
|
next_state = SKIP;
|
end
|
end
|
/* SBIC, SBIS, SBI, CBI are not implemented */
|
/* SBIC, SBIS, SBI, CBI are not implemented */
|
16'b1111_0xxx_xxxx_xxxx: begin
|
16'b1111_0xxx_xxxx_xxxx: begin
|
/* BRBS - BRBC */
|
/* BRBS - BRBC */
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
if(sreg_read ^ pmem_d[10]) begin
|
if(sreg_read ^ pmem_d[10]) begin
|
pc_sel = PC_SEL_KS;
|
pc_sel = PC_SEL_KS;
|
next_state = STALL;
|
next_state = STALL;
|
end else
|
end else
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
end
|
end
|
/* BREQ, BRNE, BRCS, BRCC, BRSH, BRLO, BRMI, BRPL, BRGE, BRLT,
|
/* BREQ, BRNE, BRCS, BRCC, BRSH, BRLO, BRMI, BRPL, BRGE, BRLT,
|
* BRHS, BRHC, BRTS, BRTC, BRVS, BRVC, BRIE, BRID are replaced
|
* BRHS, BRHC, BRTS, BRTC, BRVS, BRVC, BRIE, BRID are replaced
|
* with BRBS/BRBC */
|
* with BRBS/BRBC */
|
16'b1001_00xx_xxxx_1100, /* X */
|
16'b1001_00xx_xxxx_1100, /* X */
|
16'b1001_00xx_xxxx_1101, /* X+ */
|
16'b1001_00xx_xxxx_1101, /* X+ */
|
16'b1001_00xx_xxxx_1110, /* -X */
|
16'b1001_00xx_xxxx_1110, /* -X */
|
16'b1001_00xx_xxxx_1001, /* Y+ */
|
16'b1001_00xx_xxxx_1001, /* Y+ */
|
16'b1001_00xx_xxxx_1010, /* -Y */
|
16'b1001_00xx_xxxx_1010, /* -Y */
|
16'b10x0_xxxx_xxxx_1xxx, /* Y+q */
|
16'b10x0_xxxx_xxxx_1xxx, /* Y+q */
|
16'b1001_00xx_xxxx_0001, /* Z+ */
|
16'b1001_00xx_xxxx_0001, /* Z+ */
|
16'b1001_00xx_xxxx_0010, /* -Z */
|
16'b1001_00xx_xxxx_0010, /* -Z */
|
16'b10x0_xxxx_xxxx_0xxx: /* Z+q */
|
16'b10x0_xxxx_xxxx_0xxx: /* Z+q */
|
begin
|
begin
|
casex({pmem_d[12], pmem_d[3:0]})
|
casex({pmem_d[12], pmem_d[3:0]})
|
5'b1_1100: dmem_sel = DMEM_SEL_X;
|
5'b1_1100: dmem_sel = DMEM_SEL_X;
|
5'b1_1101: dmem_sel = DMEM_SEL_XPLUS;
|
5'b1_1101: dmem_sel = DMEM_SEL_XPLUS;
|
5'b1_1110: dmem_sel = DMEM_SEL_XMINUS;
|
5'b1_1110: dmem_sel = DMEM_SEL_XMINUS;
|
5'b1_1001: dmem_sel = DMEM_SEL_YPLUS;
|
5'b1_1001: dmem_sel = DMEM_SEL_YPLUS;
|
5'b1_1010: dmem_sel = DMEM_SEL_YMINUS;
|
5'b1_1010: dmem_sel = DMEM_SEL_YMINUS;
|
5'b0_1xxx: dmem_sel = DMEM_SEL_YQ;
|
5'b0_1xxx: dmem_sel = DMEM_SEL_YQ;
|
5'b1_0001: dmem_sel = DMEM_SEL_ZPLUS;
|
5'b1_0001: dmem_sel = DMEM_SEL_ZPLUS;
|
5'b1_0010: dmem_sel = DMEM_SEL_ZMINUS;
|
5'b1_0010: dmem_sel = DMEM_SEL_ZMINUS;
|
5'b0_0xxx: dmem_sel = DMEM_SEL_ZQ;
|
5'b0_0xxx: dmem_sel = DMEM_SEL_ZQ;
|
endcase
|
endcase
|
if(pmem_d[9]) begin
|
if(pmem_d[9]) begin
|
/* ST */
|
/* ST */
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
dmem_we = 1'b1;
|
dmem_we = 1'b1;
|
end else begin
|
end else begin
|
/* LD */
|
/* LD */
|
next_state = WRITEBACK;
|
next_state = WRITEBACK;
|
end
|
end
|
end
|
end
|
16'b1011_0xxx_xxxx_xxxx: begin
|
16'b1011_0xxx_xxxx_xxxx: begin
|
/* IN */
|
/* IN */
|
io_re = 1'b1;
|
io_re = 1'b1;
|
next_state = WRITEBACK;
|
next_state = WRITEBACK;
|
end
|
end
|
16'b1011_1xxx_xxxx_xxxx: begin
|
16'b1011_1xxx_xxxx_xxxx: begin
|
/* OUT */
|
/* OUT */
|
io_we = 1'b1;
|
io_we = 1'b1;
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
end
|
end
|
16'b1001_00xx_xxxx_xxxx: begin
|
16'b1001_00xx_xxxx_xxxx: begin
|
if(pmem_d[3:0] == 4'hf) begin
|
if(pmem_d[3:0] == 4'hf) begin
|
if(pmem_d[9]) begin
|
if(pmem_d[9]) begin
|
/* PUSH */
|
/* PUSH */
|
push = 1'b1;
|
push = 1'b1;
|
dmem_sel = DMEM_SEL_SP_R;
|
dmem_sel = DMEM_SEL_SP_R;
|
dmem_we = 1'b1;
|
dmem_we = 1'b1;
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
end else begin
|
end else begin
|
/* POP */
|
/* POP */
|
pop = 1'b1;
|
pop = 1'b1;
|
dmem_sel = DMEM_SEL_SP_R;
|
dmem_sel = DMEM_SEL_SP_R;
|
next_state = WRITEBACK;
|
next_state = WRITEBACK;
|
end
|
end
|
end else if(pmem_d[3:0] == 4'h0) begin
|
end else if(pmem_d[3:0] == 4'h0) begin
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
if(pmem_d[9])
|
if(pmem_d[9])
|
/* STS */
|
/* STS */
|
next_state = STS;
|
next_state = STS;
|
else
|
else
|
/* LDS */
|
/* LDS */
|
next_state = LDS1;
|
next_state = LDS1;
|
end
|
end
|
end
|
end
|
16'b1001_0101_000x_1000: begin
|
16'b1001_0101_000x_1000: begin
|
/* RET - RETI (treated as RET) */
|
/* RET - RETI (treated as RET) */
|
/* TODO: in which order should we pop the bytes? */
|
/* TODO: in which order should we pop the bytes? */
|
dmem_sel = DMEM_SEL_SP_PCH;
|
dmem_sel = DMEM_SEL_SP_PCH;
|
pop = 1'b1;
|
pop = 1'b1;
|
next_state = RET1;
|
next_state = RET1;
|
end
|
end
|
16'b1001_0101_1100_1000: begin
|
16'b1001_0101_1100_1000: begin
|
/* LPM */
|
/* LPM */
|
pmem_selz = 1'b1;
|
pmem_selz = 1'b1;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
next_state = LPM;
|
next_state = LPM;
|
end
|
end
|
16'b1001_0100_0000_1001: begin
|
16'b1001_0100_0000_1001: begin
|
/* IJMP */
|
/* IJMP */
|
pc_sel = PC_SEL_Z;
|
pc_sel = PC_SEL_Z;
|
next_state = STALL;
|
next_state = STALL;
|
end
|
end
|
16'b1001_0101_0000_1001: begin
|
16'b1001_0101_0000_1001: begin
|
/* ICALL */
|
/* ICALL */
|
/* TODO: in which order should we push the bytes? */
|
/* TODO: in which order should we push the bytes? */
|
dmem_sel = DMEM_SEL_SP_PCL;
|
dmem_sel = DMEM_SEL_SP_PCL;
|
dmem_we = 1'b1;
|
dmem_we = 1'b1;
|
push = 1'b1;
|
push = 1'b1;
|
next_state = ICALL;
|
next_state = ICALL;
|
end
|
end
|
default: begin
|
default: begin
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
normal_en = 1'b1;
|
normal_en = 1'b1;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
end
|
end
|
endcase
|
endcase
|
end
|
end
|
RCALL: begin
|
RCALL: begin
|
dmem_sel = DMEM_SEL_SP_PCH;
|
dmem_sel = DMEM_SEL_SP_PCH;
|
dmem_we = 1'b1;
|
dmem_we = 1'b1;
|
push = 1'b1;
|
push = 1'b1;
|
pc_sel = PC_SEL_KL;
|
pc_sel = PC_SEL_KL;
|
next_state = STALL;
|
next_state = STALL;
|
end
|
end
|
ICALL: begin
|
ICALL: begin
|
dmem_sel = DMEM_SEL_SP_PCH;
|
dmem_sel = DMEM_SEL_SP_PCH;
|
dmem_we = 1'b1;
|
dmem_we = 1'b1;
|
push = 1'b1;
|
push = 1'b1;
|
pc_sel = PC_SEL_Z;
|
pc_sel = PC_SEL_Z;
|
next_state = STALL;
|
next_state = STALL;
|
end
|
end
|
RET1: begin
|
RET1: begin
|
pc_sel = PC_SEL_DMEMH;
|
pc_sel = PC_SEL_DMEMH;
|
dmem_sel = DMEM_SEL_SP_PCL;
|
dmem_sel = DMEM_SEL_SP_PCL;
|
pop = 1'b1;
|
pop = 1'b1;
|
next_state = RET2;
|
next_state = RET2;
|
end
|
end
|
RET2: begin
|
RET2: begin
|
pc_sel = PC_SEL_DMEML;
|
pc_sel = PC_SEL_DMEML;
|
next_state = RET3;
|
next_state = RET3;
|
end
|
end
|
RET3: begin
|
RET3: begin
|
pc_sel = PC_SEL_DEC;
|
pc_sel = PC_SEL_DEC;
|
next_state = STALL;
|
next_state = STALL;
|
end
|
end
|
LPM: begin
|
LPM: begin
|
lpm_en = 1'b1;
|
lpm_en = 1'b1;
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
next_state = NORMAL;
|
next_state = NORMAL;
|
end
|
end
|
STS: begin
|
STS: begin
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
dmem_sel = DMEM_SEL_PMEM;
|
dmem_sel = DMEM_SEL_PMEM;
|
dmem_we = 1'b1;
|
dmem_we = 1'b1;
|
next_state = NORMAL;
|
next_state = NORMAL;
|
end
|
end
|
LDS1: begin
|
LDS1: begin
|
dmem_sel = DMEM_SEL_PMEM;
|
dmem_sel = DMEM_SEL_PMEM;
|
regmem_ce = 1'b0;
|
regmem_ce = 1'b0;
|
next_state = LDS2;
|
next_state = LDS2;
|
end
|
end
|
LDS2: begin
|
LDS2: begin
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
normal_en = 1'b1;
|
|
lds_writeback = 1'b1;
|
lds_writeback = 1'b1;
|
next_state = NORMAL;
|
next_state = NORMAL;
|
end
|
end
|
SKIP: begin
|
SKIP: begin
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
/* test for STS and LDS */
|
/* test for STS and LDS */
|
if((pmem_d[15:10] == 6'b100100) & (pmem_d[3:0] == 4'h0))
|
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 */
|
next_state = STALL; /* 2-word instruction, skip the second word as well */
|
else
|
else
|
next_state = NORMAL; /* 1-word instruction */
|
next_state = NORMAL; /* 1-word instruction */
|
end
|
end
|
STALL: begin
|
STALL: begin
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
next_state = NORMAL;
|
next_state = NORMAL;
|
end
|
end
|
WRITEBACK: begin
|
WRITEBACK: begin
|
pmem_ce = 1'b1;
|
pmem_ce = 1'b1;
|
pc_sel = PC_SEL_INC;
|
pc_sel = PC_SEL_INC;
|
normal_en = 1'b1;
|
normal_en = 1'b1;
|
next_state = NORMAL;
|
next_state = NORMAL;
|
end
|
end
|
endcase
|
endcase
|
end
|
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", {1'b0, T, H, S, V, N, Z, C});
|
|
$display("%x", SP[15:8]);
|
|
$display("%x", SP[7:0]);
|
|
$display("%x", PC[14: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];
|
|
reg I;
|
|
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
|
endmodule
|
|
|