OpenCores
URL https://opencores.org/ocsvn/softavrcore/softavrcore/trunk

Subversion Repositories softavrcore

[/] [softavrcore/] [trunk/] [core/] [avr_core.v] - Rev 2

Compare with Previous | Blame | View Log

/*****************************************************************************/
/* avr_core.v								     */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* (c) 2019-2020; Andras Pal <apal@szofi.net>				     */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Portions of the code got inspiration from the Navre project		     */
/* (https://opencores.org/projects/navre)				     */
/*****************************************************************************/
 
`define AVR_200
//`define AVR_250
//`define AVR_310
//`define AVR_350
//`define AVR_400
//`define AVR_500
//`define AVR_510
 
`define AVR_INITIAL
 
/*****************************************************************************/
 
module avr_core
#(	parameter pmem_width = 9, /* PMEM address bus width (16-bit instr)   */
	parameter dmem_width = 9, /* RAM address bus width (bytes)	     */
	parameter interrupt  = 1,
	parameter intr_width = 2  /* number of interrupt vector bits	     */
 ) 
 (	input	clk,
	input	rst,
 
	output	pmem_ce,
	output	[pmem_width-1:0] pmem_a,
	input	[15:0] pmem_d,
 
	output	dmem_re,
	output	dmem_we,
	output	[dmem_width-1:0] dmem_a,
	input	[7:0] dmem_di,
	output	[7:0] dmem_do,
 
	output	io_re,
	output	io_we,
	output	[5:0] io_a,
	input	[7:0] io_di,
	output	[7:0] io_do,
 
	input	in_iflag,
	input	[intr_width-1:0] in_ivect
 );
 
/*****************************************************************************/
 
/******************************************************************************
	     |	avr2	avr25	avr31	avr35	avr4	avr5	avr51
-------------+-----------------------------------------------------------------
MOVW	     |		  X	   X	  X	  X	  X	  X
LPM  Rd,Z(+) |		  X	   X	  X	  X	  X	  X
ELPM	     |			   X	  		  	  X
ELPM Rd,Z(+) |							  X
JMP/CALL     |			   X	  X		  X	  X
SPM	     |		  X		  X	  X	  X	  X 	
MUL	     |					  X	  X	  X
******************************************************************************/
 
`ifdef AVR_250
`define AVR_HAVE_MOVW
`define AVR_HAVE_LPMZ
`define	AVR_HAVE_SPM
`endif
 
`ifdef AVR_310
`define AVR_HAVE_MOVW
`define AVR_HAVE_LPMZ
`define AVR_HAVE_ELPM
`define AVR_HAVE_22BITPC
`endif
 
`ifdef AVR_350
`define AVR_HAVE_MOVW
`define AVR_HAVE_LPMZ
`define AVR_HAVE_22BITPC
`define	AVR_HAVE_SPM
`endif
 
`ifdef AVR_400
`define AVR_HAVE_MOVW
`define AVR_HAVE_LPMZ
`define	AVR_HAVE_SPM
`define AVR_HAVE_MUL
`endif
 
`ifdef AVR_500
`define AVR_HAVE_MOVW
`define AVR_HAVE_LPMZ
`define AVR_HAVE_22BITPC
`define	AVR_HAVE_SPM
`define AVR_HAVE_MUL
`endif
 
`ifdef AVR_510
`define AVR_HAVE_MOVW
`define AVR_HAVE_LPMZ
`define AVR_HAVE_ELPM
`define AVR_HAVE_ELPMZ
`define AVR_HAVE_22BITPC
`define	AVR_HAVE_SPM
`define AVR_HAVE_MUL
`endif
 
/*****************************************************************************/
 
localparam MEM_OFFSET = 96;
 
/*****************************************************************************/
 
`ifdef AVR_INITIAL
localparam	STATE_INITIAL	= 4'd0;
localparam	STATE_STALL	= 4'd1;
`else
localparam	STATE_STALL	= 4'd0;
`endif
localparam	STATE_NORMAL	= 4'd2;
localparam	STATE_TWOWORD	= 4'd3;
localparam	STATE_SKIP	= 4'd4;
localparam	STATE_LD	= 4'd5;
localparam	STATE_CALL	= 4'd6;
localparam	STATE_RET	= 4'd7;
localparam	STATE_RET2	= 4'd8;
localparam	STATE_LPM	= 4'd9;
localparam	STATE_LPM2	= 4'd10;
localparam	STATE_ADIW	= 4'd11;
localparam	STATE_IO_BIT	= 4'd12;
localparam	STATE_MUL	= 4'd13;
localparam	STATE_IN	= 4'd14;
 
/*****************************************************************************/
 
/* CPU core state registers: */
reg [pmem_width-1:0] PC;	/* <= 0 */
reg [3:0] state;		/* <= 0: hence, STALL state should be zero! */
 
wire [15:0] INSTR;
reg [15:0] PREVI;
 
reg [7:0] GPR[0:31];
 
`ifdef AVR_INITIAL
`ifdef SIMULATOR
localparam	init_depth = 3;
`else
localparam	init_depth = 8;
`endif
reg [init_depth-1:0] init_count;
`endif
 
`ifdef SIMULATOR
integer i;
initial begin
	for ( i=0;i<32;i=i+1)
		GPR[i] <= 8'hA0 + i;
end
`endif
 
wire [15:0] RX = { GPR[27], GPR[26] };
wire [15:0] RY = { GPR[29], GPR[28] };
wire [15:0] RZ = { GPR[31], GPR[30] };
 
reg I, T, H, S, V, N, Z, C;	/* initialized as <= 0  */
 
wire [7:0] SREG = { I, T, H, S, V, N, Z, C };
 
reg [15:0] SP;			/* initialized as <= 0	*/
 
 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
reg nI, nT, nH, nS, nV, nN, nZ, nC;
wire [7:0] nSREG = { nI, nT, nH, nS, nV, nN, nZ, nC };
 
reg [7:0] R;			/* generic result for writeback 	*/
reg [7:0] R_high;		/* result for 1-word writeback - high byte */
 
wire [15:0] RX_inc_dec = (INSTR[0] ? RX + 1 : RX - 1);
wire [15:0] RY_inc_dec = (INSTR[0] ? RY + 1 : RY - 1);
wire [15:0] RZ_inc_dec = (INSTR[0] ? RZ + 1 : RZ - 1);
wire [15:0] RZ_inc     = (INSTR[0] ? RZ + 1 :   RZ  );
 
/* arithmetic and logic operators, followed by an 8-bit immediate operand: */
/* only these instruction imply destination registers r16 ... r31 */
wire immediate = (INSTR[15:14] == 2'b01  ) | 	/* ANDI, ORI, SBCI, SUBI */
		 (INSTR[15:12] == 4'b0011) |	/* CPI			 */
		 (INSTR[15:12] == 4'b1110);	/* LDI			 */
 
wire [4:0] Rd_normal = { immediate | INSTR[8], INSTR[7:4] };
 
wire [4:0] Rd = Rd_normal;
 
wire [4:0] Rd_prev = PREVI[8:4];
wire [4:0] Rr = { INSTR[9], INSTR[3:0] };
wire [1:0] Rd16 = INSTR[5:4];
wire [1:0] Rp16 = PREVI[5:4];
wire [7:0] GPR_Rd = GPR[Rd];
wire [2:0] b = INSTR[2:0];
wire GPR_Rd_b = GPR_Rd[b];
wire [7:0] GPR_Rr = GPR[Rr];
wire [3:0] RD16 = INSTR[7:4];
wire [3:0] RR16 = INSTR[3:0];
 
/* used by: ANDI, ORI, SBCI, SUBI, CPI, LDI (i.e. where immediate == 1'b1): */
wire [7:0] K = { INSTR[11:8], INSTR[3:0] };
/* used by: LDD Rd, Y+q; LDD RD, Z+q; STD Y+q, Rd; STD Z+q, Rd: */
wire [5:0] q = { INSTR[13], INSTR[11:10], INSTR[2:0] };
/* used by: ADIW, SBIW: */
wire [5:0] K16 = { INSTR[7:6], INSTR[3:0] };
 
wire two_word_lds_sts = ((INSTR[15:10]==6'b100100) & (INSTR[3:0]==4'b0000));
 
`ifdef AVR_HAVE_22BITPC
/* this is for avr3 (avr31, avr35) and avr5 (avr51): */
wire two_word_jmp_call = ((INSTR[15:9]==7'b1001010) & (INSTR[3:2]==2'b11));
wire two_word_instr = two_word_lds_sts | two_word_jmp_call;
`else
/* otherwise (avr2, avr25 and avr4): */
wire two_word_instr = two_word_lds_sts;
`endif
 
`ifdef AVR_HAVE_MUL
wire [4:0] Rd_mul = INSTR[15] ? INSTR[8:4] : ~INSTR[8] ? { 1'b1, INSTR[7:4] } : { 2'b10, INSTR[6:4] };
wire [4:0] Rr_mul = INSTR[15] ? Rr         : ~INSTR[8] ? { 1'b1, INSTR[3:0] } : { 2'b10, INSTR[2:0] };
wire [7:0] GPR_Rd_mul = GPR[Rd_mul];
wire [7:0] GPR_Rr_mul = GPR[Rr_mul];
reg [7:0] mul_rd;
reg [7:0] mul_rr;
reg [15:0] product;
/* FMUL, FMULS, FMULSU: */
wire fmulxx = ~INSTR[15] & (INSTR[3]  | INSTR[7]); 
/* MULS, FMULS: */
wire xmulsx = ~INSTR[15] & (~INSTR[8] | ({INSTR[7],INSTR[3]} == 2'b10));
/* MULS, MULSU, FMULS, FMULSU: */
wire xmulsu = ~INSTR[15] & (~INSTR[8] | ({INSTR[7],INSTR[3]} != 2'b01));
reg [2:0] mul_type; /* = { fmulxx, xmulsx, xmulsu }; */
`endif
 
/* stage2 temporary registers: */
 
reg [2:0] writeback;
reg change_z;
reg update_nsz;
reg [pmem_width-1:0] pc_next;
reg [15:0] sp_next;
reg sp_update;
 
reg [4:0] Rd_ld_save;
reg lpm_z_low; 
 
localparam	WRITEBACK_NONE   = 3'd0;
localparam	WRITEBACK_GPR    = 3'd1;
localparam	WRITEBACK_ZINC   = 3'd4;
localparam	WRITEBACK_ZY     = 3'd5;
localparam	WRITEBACK_X      = 3'd6;
 
reg [3:0] next_state;
 
reg  [15:0] pc_call_next;
reg  [15:0] pc_call;
 
wire [15:0] pc_full = { {(16-pmem_width){1'b0}}, PC };
wire [15:0] pc_full_dec = { {(16-pmem_width){1'b0}}, PC - 1'b1 };
 
/* data memory interface: */
reg [15:0] d_addr;
reg d_read;
reg d_write;
reg [7:0] d_out;
 
/* interrupts: */
wire is_tail_reti;
wire is_interrupt;
 
reg iflag;
reg [intr_width-1:0] ivect;
 
generate 
	if ( interrupt ) begin
		assign is_tail_reti = INSTR[4] & iflag;
		assign is_interrupt = I & iflag & (state==STATE_NORMAL); 
	end else begin
		assign is_tail_reti = 0;
		assign is_interrupt = 0;
	end 
endgenerate
 
/* Instructions performing memory I/O via the dmem_* bus: */
 
// 16'b10q0_qq0d_dddd_0qqq		LD Rd, Z+q
// 16'b10q0_qq0d_dddd_1qqq		LD Rd, Y+q
// 16'b10q0_qq1d_dddd_0qqq		ST Z+q, Rd
// 16'b10q0_qq1d_dddd_1qqq		ST Y+q, Rd
// 16'b1001_000d_dddd_0001		LD Rd, Z++
// 16'b1001_000d_dddd_1001		LD Rd, Y++
// 16'b1001_001d_dddd_0001		ST Z++, Rd
// 16'b1001_001d_dddd_1001		ST Y++, Rd	
// 16'b1001_000d_dddd_0010		LD Rd, --Z
// 16'b1001_000d_dddd_1010		LD Rd, --Y
// 16'b1001_001d_dddd_0010		ST --Z, Rd
// 16'b1001_001d_dddd_1010		ST --Y, Rd
// 16'b1001_000d_dddd_1100		LD Rd, X
// 16'b1001_000d_dddd_1101		LD Rd, X++
// 16'b1001_000d_dddd_1110		LD Rd, --X
// 16'b1001_001d_dddd_1100		ST X, Rd
// 16'b1001_001d_dddd_1101		ST X++, Rd
// 16'b1001_001d_dddd_1110		ST --X, Rd
// 16'b1001_000d_dddd_1111		POP Rd	== LD Rd, ++SP
// 16'b1001_001d_dddd_1111		PUSH Rd == ST SP--, Rd
 
// 16'b1001_000d_dddd_1101              LD Rd, X++
// 16'b1001_000d_dddd_1110              LD Rd, --X
// 16'b1001_001d_dddd_1101              ST X++, Rd
// 16'b1001_001d_dddd_1110              ST --X, Rd
 
// 16'b1001_000d_dddd_1001              LD Rd, Y++
// 16'b1001_000d_dddd_1010              LD Rd, --Y
// 16'b1001_001d_dddd_1001              ST Y++, Rd      
// 16'b1001_001d_dddd_1010              ST --Y, Rd
 
// 16'b1001_000d_dddd_0001              LD Rd, Z++
// 16'b1001_000d_dddd_0010              LD Rd, --Z
// 16'b1001_001d_dddd_0001              ST Z++, Rd
// 16'b1001_001d_dddd_0010              ST --Z, Rd
 
// 16'b1001_000d_dddd_01x1		LPM Rd, Z++
 
// INSTR[15:10] == 6'b100100
// X increased/decreased: INSTR[3:2] == 2'b11	
// Y increased/decreased: INSTR[3:2] == 2'b10
// Z increased/decreased: INSTR[3:2] == 2'b00
// Z increased		  INSTR[3:2] == 2'b01	
 
/* setting up memory interface lines (d_addr, d_out, d_read and d_write): */
 
always @(*) begin
 
	d_out = 0;
	d_addr = 0;
	d_read = 0;
	d_write = 0;
 
	case (state)
 
	    STATE_NORMAL: begin
 
		casex (INSTR)
 
		    16'b10x0_xx0x_xxxx_xxxx: begin
			/* LD Rd, Z+q */
			/* LD Rd, Y+q */
			d_addr = (~INSTR[3]?RZ:RY) + q;
			d_read = 1;
			end
 
		    16'b10x0_xx1x_xxxx_xxxx: begin
			/* ST Z+q, Rd */
			/* ST Y+q, Rd */
			d_addr = (~INSTR[3]?RZ:RY) + q;
			d_out = GPR_Rd;
			d_write = 1;
			end
 
		    16'b1001_000x_xxxx_x001: begin
			/* LD Rd, Z++ */
			/* LD Rd, Y++ */
			d_addr = (~INSTR[3]?RZ:RY);
			d_read = 1;
		    	end
 
		    16'b1001_001x_xxxx_x001: begin
			/* ST Z++, Rd */
			/* ST Y++, Rd */
			d_addr = (~INSTR[3]?RZ:RY);
			d_out = GPR_Rd;
			d_write = 1;
		    	end
 
		    16'b1001_000x_xxxx_x010: begin
			/* LD Rd, --Z */
			/* LD Rd, --Y */
			d_addr = (~INSTR[3]?RZ:RY) - 1;
			d_read = 1;
		    	end
 
		    16'b1001_001x_xxxx_x010: begin
			/* ST --Z, Rd */
			/* ST --Y, Rd */
			d_addr = (~INSTR[3]?RZ:RY) - 1;
			d_out = GPR_Rd;
			d_write = 1;
		    	end
 
		    16'b1001_000x_xxxx_110x: begin
			/* LD Rd, X */
			/* LD Rd, X++ */
			d_addr = RX;
			d_read = 1;
			end
		    16'b1001_000x_xxxx_1110: begin
			/* LD Rd, --X */
			d_addr = RX - 1;
			d_read = 1;
			end
 
		    16'b1001_001x_xxxx_110x: begin
			/* ST X, Rd */
			/* ST X++, Rd */
			d_addr = RX;
			d_out = GPR_Rd;
			d_write = 1;
			end
 
		    16'b1001_001x_xxxx_1110: begin
			/* ST --X, Rd */
			d_addr = RX - 1;
			d_out = GPR_Rd;
			d_write = 1;
			end
 
		    16'b1001_000x_xxxx_1111: begin
			/* POP Rd -- LD Rd, ++SP */
			d_addr = SP + 1;
			d_read = 1;
			end
 
		    16'b1001_001x_xxxx_1111: begin
			/* PUSH Rd -- ST SP--, Rd */
			d_addr = SP;
			d_out = GPR_Rd;
			d_write = 1;
			end
 
		    16'b1101_xxxx_xxxx_xxxx, 
		    16'b1001_0101_0000_1001: begin
			/* RCALL */
			/* ICALL */
			d_addr = SP;
			d_out = pc_full[15:8];
			d_write = 1;
			end
 
		    16'b1001_0101_000x_1000: begin
			/* RET, RETI */
			d_addr = SP + 1;
			d_read = 1;
			end
 
		endcase
 
		if (is_interrupt) begin
			d_addr = SP;
			d_out = pc_full_dec[15:8];
			d_write = 1;
		end
 
		end
 
	    STATE_TWOWORD: begin
		casex(PREVI)
 
		    16'b1001_000x_xxxx_0000: begin
			/* LDS Rd, 0xXXXX */
			d_addr = INSTR;
			d_read = 1;
			end
 
		    16'b1001_001x_xxxx_0000: begin
			/* STS 0xXXXX, Rd */
			d_addr = INSTR;
			d_out = GPR[Rd_prev];
			d_write = 1;
			end
 
		`ifdef AVR_HAVE_22BITPC
		    16'b1001_010x_xxxx_111x: begin
			/* CALL k */
			d_addr = SP;
			d_out = pc_full[15:8];
			d_write = 1;
			end
		`endif
 
		endcase
 
		end
 
	    STATE_CALL: begin
		/* RCALL */
		/* ICALL */
		d_addr = SP;
		d_out = pc_full[7:0];
		d_write = 1;
		end
 
	    STATE_RET: begin
		d_addr = SP + 1;
		d_read = 1;
		end
 
	endcase		
end
 
assign  dmem_a  = d_addr[dmem_width-1:0] - MEM_OFFSET;
assign	dmem_re = d_read;
assign	dmem_we = d_write;
assign	dmem_do = d_out; 
 
/* IN/OUT assignments: */
 
/* used by: IN, OUT, SBIC, SBIS, CBI, SBI: */
// 16'b1011_0aad_dddd_aaaa: 	/* IN	*/
// 16'b1011_1aad_dddd_aaaa:	/* OUT	*/
// 16'b1001_1000_aaaa_abbb: 	/* CBI	*/
// 16'b1001_1010_aaaa_abbb: 	/* SBI	*/
// 16'b1001_1001_aaaa_abbb:	/* SBIC */
// 16'b1001_1011_aaaa_abbb:	/* SBIS */
 
reg [7:0] Rio;
 
wire 	[5:0] a = INSTR[13] ? {INSTR[10:9], INSTR[3:0]} : {1'b0, INSTR[7:3]};
wire	io_act = (INSTR[15:12]==4'b1011) & (state == STATE_NORMAL);
assign	io_a = (state == STATE_IO_BIT ? {1'b0, PREVI[7:3]} : a);
assign	io_do = (state == STATE_IO_BIT ? Rio : GPR_Rd);
assign	io_re = (io_act & (INSTR[11]==1'b0)) | ((state == STATE_NORMAL) & (INSTR[15:10]==6'b100110));
assign	io_we = (io_act & (INSTR[11]==1'b1)) | (state == STATE_IO_BIT);
 
 
wire [7:0] Rin = (a==61?(SP[7:0]):(a==62?(SP[15:8]):(a==63?SREG:io_di)));
 
/*
reg [7:0] Rin;
always @(*) begin
	if (a==6'b111101) begin
		Rin = SP[7:0];
	end else if (a==6'b111110) begin
		Rin = SP[15:8];
	end else if (a==6'b111111) begin
		Rin = SREG;
	end else begin
		Rin = io_di;
	end
end
*/
 
/* CPU instruction pipeline, stage 1: */
 
assign pmem_ce = 1'b1;
assign pmem_a = PC;
/* always @(posedge clk) pmem_d <= FLASH[pmem_a]; */	
assign INSTR = pmem_d;
 
always @(posedge clk) begin
 
	PREVI <= INSTR;
 
	iflag <= in_iflag;
	ivect <= in_ivect;
 
end /* always @(posedge clk) */
 
/* CPU instruction pipeline, stage 2: */
 
always @(*) begin
 
	R = 0;
	R_high = 0;
 
	writeback = WRITEBACK_NONE ;
	change_z = 1;
	update_nsz = 0;
 
	next_state = STATE_NORMAL;
	pc_next = PC;
	sp_next = SP;
	sp_update = 0;
 
	pc_call_next = 0;
 
	{ nI, nT, nH, nS, nV, nN, nZ, nC } = SREG;
 
	case(state)
 
	    STATE_NORMAL: begin
 
		casex(INSTR) 
 
		`ifdef AVR_HAVE_MOVW
		    16'b0000_0001_xxxx_xxxx: begin
			R = GPR[2*RR16+0];
                	R_high = GPR[2*RR16+1];
			pc_next = PC + 1;
			end
		`endif
 
		`ifdef AVR_HAVE_MUL
		    16'b1001_11xx_xxxx_xxxx,
		    16'b0000_001x_xxxx_xxxx: begin
			/* MUL */
			/* MULS */
			/* MULSU */
			/* FMUL */
			/* FMULS */
			/* FMULSU */
			next_state = STATE_MUL;
			end
		`endif
 
		    16'b000x_10xx_xxxx_xxxx, /* subtract */
		    16'b000x_01xx_xxxx_xxxx: /* compare  */ begin
			/* SUB - SBC / CP - CPC */
			{nC, R} = GPR_Rd - GPR_Rr - (~INSTR[12] & C);
			nH = (~GPR_Rd[3] & GPR_Rr[3])|(GPR_Rr[3] & R[3])|(R[3] & ~GPR_Rd[3]);
			nV = (GPR_Rd[7] & ~GPR_Rr[7] & ~R[7])|(~GPR_Rd[7] & GPR_Rr[7] & R[7]);
			update_nsz = 1;
			if (~INSTR[12])
				change_z = 1'b0;
			if (INSTR[11])
				writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b000x_11xx_xxxx_xxxx: begin
			/* ADD - ADC */
			{nC, R} = GPR_Rd + GPR_Rr + (INSTR[12] & C);
			nH = (GPR_Rd[3] & GPR_Rr[3])|(GPR_Rr[3] & ~R[3])|(~R[3] & GPR_Rd[3]);
			nV = (GPR_Rd[7] & GPR_Rr[7] & ~R[7])|(~GPR_Rd[7] & ~GPR_Rr[7] & R[7]);
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b010x_xxxx_xxxx_xxxx, /* subtract */
		    16'b0011_xxxx_xxxx_xxxx: /* compare  */ begin
			/* SUBI - SBCI / CPI */
			{nC, R} = GPR_Rd - K - (~INSTR[12] & C);
			nH = (~GPR_Rd[3] & K[3])|(K[3] & R[3])|(R[3] & ~GPR_Rd[3]);
			nV = (GPR_Rd[7] & ~K[7] & ~R[7])|(~GPR_Rd[7] & K[7] & R[7]);
			update_nsz = 1;
			if (~INSTR[12])
				change_z = 1'b0;
			if (INSTR[14])
				writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
 
		    16'b0111_xxxx_xxxx_xxxx: begin
			/* ANDI Rd, K; */
			R = GPR_Rd & K;
			nV = 1'b0; 
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
 
		    16'b0110_xxxx_xxxx_xxxx: begin
			/* ORI Rd, K; */
			R = GPR_Rd | K;
			nV = 1'b0; 
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
 
		    16'b0010_00xx_xxxx_xxxx: begin
			/* AND */
			R = GPR_Rd & GPR_Rr;
	                nV = 1'b0;
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b0010_01xx_xxxx_xxxx: begin
			/* EOR */
			R = GPR_Rd ^ GPR_Rr;
	                nV = 1'b0;
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b0010_10xx_xxxx_xxxx: begin
			/* OR */
			R = GPR_Rd | GPR_Rr;
	                nV = 1'b0;
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b0010_11xx_xxxx_xxxx: begin
			/* MOV */
			R = GPR_Rr;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1001_010x_xxxx_0000: begin
			/* COM */
			R = ~GPR_Rd;
			nV = 1'b0;
			nC = 1'b1;
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1001_010x_xxxx_0001: begin
			/* NEG */
			{nC, R} = 8'h00 - GPR_Rd;
			nH = R[3] | GPR_Rd[3];
			nV = (R == 8'h80);
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1001_010x_xxxx_0011: begin
			/* INC */
			R = GPR_Rd + 8'd1;
			nV = (R == 8'h80);
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1001_010x_xxxx_1010: begin
			/* DEC */
			R = GPR_Rd - 8'd1;
			nV = (R == 8'h7f);
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1001_010x_xxxx_011x: begin
			/* LSR - ROR */
			R = {INSTR[0] & C, GPR_Rd[7:1]};
			nC = GPR_Rd[0];
			nV = R[7] ^ GPR_Rd[0];
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1001_010x_xxxx_0101: begin
			/* ASR */
			R = {GPR_Rd[7], GPR_Rd[7:1]};
			nC = GPR_Rd[0];
			nV = R[7] ^ GPR_Rd[0];
			update_nsz = 1;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1001_010x_xxxx_0010: begin
			/* SWAP */
			R = {GPR_Rd[3:0], GPR_Rd[7:4]};
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1001_0100_xxxx_1000: begin
			/* BSET - BCLR */
			case(INSTR[7:4])
				4'b0000: nC = 1'b1;
				4'b0001: nZ = 1'b1;
				4'b0010: nN = 1'b1;
				4'b0011: nV = 1'b1;
				4'b0100: nS = 1'b1;
				4'b0101: nH = 1'b1;
				4'b0110: nT = 1'b1;
				4'b0111: nI = 1'b1;
				4'b1000: nC = 1'b0;
				4'b1001: nZ = 1'b0;
				4'b1010: nN = 1'b0;
				4'b1011: nV = 1'b0;
				4'b1100: nS = 1'b0;
				4'b1101: nH = 1'b0;
				4'b1110: nT = 1'b0;
				4'b1111: nI = 1'b0;
			endcase
			pc_next = PC + 1;
			end
 
		    16'b1001_011x_xxxx_xxxx: begin
			/* SBIW */
			/* ADIW */
			if(INSTR[8]) begin	/* SBIW */
				{nC, R} = GPR[24+2*Rd16] - K16;
			end else begin		/* ADIW */
				{nC, R} = GPR[24+2*Rd16] + K16;
			end
			nZ = (R==0);
			next_state = STATE_ADIW;
			end
 
		    16'b1001_00xx_xxxx_0000: begin
			/* LDS Rd, 0xXXXX */
			/* STS 0xXXXX, Rd */
			pc_next = PC + 1;
			next_state = STATE_TWOWORD;
			end
 
		`ifdef AVR_HAVE_22BITPC
		    16'b1001_010x_xxxx_11xx: begin
			/* JMP k */
			/* CALL k */
			pc_next = PC + 1;
			next_state = STATE_TWOWORD;
			end
		`endif
 
		    16'b10x0_xx0x_xxxx_xxxx: begin
			/* LD Rd, Z+q; */
			/* LD Rd, Y+q; */
			next_state = STATE_LD; 
			end
 
		    16'b10x0_xx1x_xxxx_xxxx: begin
			/* ST Z+q, Rd */
			/* ST Y+q, Rd */
			pc_next = PC + 1;
			end
 
		    16'b1001_000x_xxxx_x001: begin
			/* LD Rd, Z++ */
			/* LD Rd, Y++ */
			next_state = STATE_LD;
			/* save dest register for the next cycle: */
			writeback = WRITEBACK_ZY;
			end
 
		    16'b1001_001x_xxxx_x001: begin
			/* ST Z++, Rd */
			/* ST Y++, Rd */
			pc_next = PC + 1;
			writeback = WRITEBACK_ZY;			
			end
 
		    16'b1001_000x_xxxx_x010: begin
			/* LD Rd, --Z */
			/* LD Rd, --Y */
			next_state = STATE_LD;
			/* save dest register for the next cycle: */
			writeback = WRITEBACK_ZY;
			end
 
		    16'b1001_001x_xxxx_x010: begin
			/* ST --Z, Rd */
			/* ST --Y, Rd */
			pc_next = PC + 1;
			writeback = WRITEBACK_ZY;			
			end
 
		    16'b1001_000x_xxxx_1100: begin
			/* LD Rd, X */
			next_state = STATE_LD;
			end
 
		    16'b1001_001x_xxxx_1100: begin
			/* ST X, Rd */
			pc_next = PC + 1;
			end
 
		    16'b1001_000x_xxxx_1101: begin
			/* LD Rd, X++ */
			writeback = WRITEBACK_X;
			next_state = STATE_LD;
			end
 
		    16'b1001_001x_xxxx_1101: begin
			/* ST X++, Rd */
			writeback = WRITEBACK_X;
			pc_next = PC + 1;
			end
 
		    16'b1001_000x_xxxx_1110: begin
			/* LD Rd, --X */
			writeback = WRITEBACK_X;
			next_state = STATE_LD;
			end
 
		    16'b1001_001x_xxxx_1110: begin
			/* ST --X, Rd */
			writeback = WRITEBACK_X;
			pc_next = PC + 1;
			end
 
		    16'b1001_000x_xxxx_1111: begin
			/* POP Rd */	/* LD Rd, ++SP */
			sp_next = SP + 1;
			sp_update = 1;
			next_state = STATE_LD;
			end
 
		    16'b1001_001x_xxxx_1111: begin
			/* PUSH Rd */	/* ST SP--, Rd */
			sp_next = SP - 1;
			sp_update = 1;
			next_state = STATE_STALL;
			end
 
		    16'b1100_xxxx_xxxx_xxxx: begin
			/* RJMP */
			next_state = STATE_STALL;
			pc_next = PC + { {4{INSTR[11]}}, INSTR[11:0] };
			end
		    16'b1001_0100_0000_1001: begin
			/* IJMP */
			next_state = STATE_STALL;
			pc_next = RZ;
			end
 
		    16'b1101_xxxx_xxxx_xxxx: begin
			/* RCALL */
			pc_call_next = pc_full + { {4{INSTR[11]}}, INSTR[11:0] };
			sp_next = SP - 1;
			sp_update = 1;
			next_state = STATE_CALL;
			end
		    16'b1001_0101_0000_1001: begin
			/* ICALL */
			pc_call_next = RZ;
			sp_next = SP - 1;
			sp_update = 1;
			next_state = STATE_CALL;
			end
 
		    16'b1001_0101_0000_1000,		/* INSTR: 0x9508 */
		    16'b1001_0101_0001_1000: begin	/* INSTR: 0x9518 */
			/* RET */
			/* RETI */
 
			if ( is_tail_reti ) begin
				// `RETI` is equivalent to `JMP ivect` if
				// there is a pending interrupt:
				next_state = STATE_STALL;
				pc_next = ivect;
			end else begin 
				// Otherwise, RETI and RET is the same...
				next_state = STATE_RET;
				sp_next = SP + 1;
				sp_update = 1;
				// ... besides that RETI sets the I flag: 
				if (INSTR[4])
					nI = 1'b1;
 
			end
 
			end
 
		    16'b1001_0101_110x_1000: begin
			/* LPM */
			/* ELPM */
			pc_call_next = PC;
			pc_next = RZ[pmem_width:1];
			next_state = STATE_LPM;
			end
 
//		`ifdef AVR_HAVE_SPM
//		    16'b1001_0101_111x_1000: begin
//			/* SPM Z */
//			/* SPM Z+ */
//			pc_call_next = PC;
//			pc_next = RZ[pmem_width:1];
//			next_state = STATE_LPM;
//			end
//		`endif
 
		`ifdef AVR_HAVE_LPMZ
		    16'b1001_000x_xxxx_01xx: begin
			/* LPM Rd, Z */
			/* LPM Rd, Z++ */
			/* ELPM Rd, Z */
			/* ELPM Rd, Z++ */
			pc_call_next = PC;
			pc_next = RZ[pmem_width:1];
			next_state = STATE_LPM;
			writeback = WRITEBACK_ZINC;
			end
		`endif	
 
		    16'b1111_0xxx_xxxx_xxxx: begin
			/* BRxS - BRxC */
			if (SREG[b] ^ INSTR[10]) begin
				next_state = STATE_STALL;
				pc_next = PC + { {9{INSTR[9]}}, INSTR[9:3] };
			end else begin
				pc_next = PC + 1;
			end
			end			
		    16'b1111_11xx_xxxx_0xxx: begin
			/* SBRC */
			/* SBRS */
			if (GPR_Rd_b == INSTR[9]) begin
				next_state = STATE_SKIP;
			end
			pc_next = PC + 1;
			end
		    16'b1001_10x0_xxxx_xxxx: begin
			/* CBI */
			/* SBI */
                        next_state = STATE_IO_BIT;
			end
		    16'b1001_10x1_xxxx_xxxx: begin
			/* SBIC */
			/* SBIS */
			if (Rin[b]==INSTR[9]) begin
				next_state = STATE_SKIP;
			end
			pc_next = PC + 1;
			end
		    16'b0001_00xx_xxxx_xxxx: begin
			/* CPSE */
			if (GPR_Rd == GPR_Rr) begin
				next_state = STATE_SKIP;
			end
			pc_next = PC + 1;
			end			
		    16'b1110_xxxx_xxxx_xxxx: begin
	                /* LDI */
			R = K;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1111_10xx_xxxx_0xxx: begin
			/* BST */
			/* BLD */
			if (INSTR[9]) begin	/* BST */
				nT = GPR_Rd_b;
			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
				writeback = WRITEBACK_GPR; 
			end
			pc_next = PC + 1;
			end
		    16'b1011_0xxx_xxxx_xxxx: begin
			/* IN */
			R = Rin;
			writeback = WRITEBACK_GPR;
			pc_next = PC + 1;
			end
		    16'b1011_1xxx_xxxx_xxxx: begin
			/* OUT */
			if (a==6'b111101) begin			/* SPL */
				sp_next[7:0] = GPR_Rd;
				sp_update = 1;
			end else if (a==6'b111110) begin	/* SPH */
				sp_next[15:8] = GPR_Rd;
				sp_update = 1;
			end else if (a==6'b111111) begin	/* SREG */
				{ nI, nT, nH, nS, nV, nN, nZ, nC } = GPR_Rd;
			end
			// in all other cases, the data flow is handled
			// by the IN/OUT persistent assignments (see earlier)
			pc_next = PC + 1;
			end
 
		    default:
			pc_next = PC + 1;
		endcase
 
		if (update_nsz) begin
			nN = R[7];
			nS = nN ^ nV;
			nZ = (R == 8'h00) & (change_z|Z);
		end
 
//		`ifdef AVR_INTERRUPT
		if (is_interrupt) begin
			// An interrupt is equivalent to a CALL, however,
			// the actual program counter needed to be saved, not 
			// the PC corresponding to the following instruction.
			// Due to the two-stage pipeline, the current 
			// instruction is PC - 1, and not PC: 
			writeback = 0;
			pc_call_next = ivect;
			sp_next = SP - 1;
			sp_update = 1;
			next_state = STATE_CALL;
			pc_next = PC - 1;
			nI = 0;
		end
//		`endif
 
		end /* STATE_NORMAL */
 
	    STATE_TWOWORD: begin
		casex(PREVI)
 
		    16'b1001_000x_xxxx_0000: begin
			/* LDS Rd, 0xXXXX */
			next_state = STATE_LD; 
			end
 
		    16'b1001_001x_xxxx_0000: begin
			/* STS 0xXXXX, Rd */
			pc_next = PC + 1;
			end
 
		`ifdef AVR_HAVE_22BITPC
		    16'b1001_010x_xxxx_110x: begin
			/* JMP K */
			next_state = STATE_STALL;
			pc_next = INSTR;
			end
		    16'b1001_010x_xxxx_111x: begin
			/* CALL K */
			pc_call_next = INSTR;
			sp_next = SP - 1;
			sp_update = 1;
			next_state = STATE_CALL;
			end
		`endif
 
		    default:
			pc_next = PC + 1;
 
		endcase			
 
		end /* STATE_TWOWORD */
 
	    STATE_STALL: begin
		pc_next = PC + 1;
		next_state = STATE_NORMAL;
		end /* STATE_STALL */
 
	    STATE_LD: begin
		pc_next = PC + 1;
		next_state = STATE_NORMAL;
		end /* STATE_LD */
 
	    STATE_CALL: begin
		sp_next = SP - 1;
		sp_update = 1;
		pc_next = pc_call; 
		next_state = STATE_STALL;
		end /* STATE_CALL */
 
	    STATE_RET: begin
		pc_call_next = { 8'h00, dmem_di };
		sp_next = SP + 1;
		sp_update = 1;
		next_state = STATE_RET2;
		end /* STATE_RET2 */
 
	    STATE_RET2: begin
		pc_next = { dmem_di, pc_call[7:0] };
		next_state = STATE_STALL;
		end /* STATE_RET2 */
 
	    STATE_SKIP: begin
		if (two_word_instr)	next_state = STATE_STALL;
		else			next_state = STATE_NORMAL;
		pc_next = PC + 1;
		end /* STATE_SKIP */
 
	    STATE_LPM: begin
		pc_next = pc_call;
		next_state = STATE_LPM2;
		end
 
	    STATE_LPM2: begin
		pc_next = PC + 1;
		next_state = STATE_NORMAL;
		end
 
	    STATE_ADIW: begin
		if (PREVI[8]) begin
			/* SBIW */
			{nC, R_high} = GPR[24+2*Rp16+1] - C;
			nV =  GPR[24+2*Rp16+1][7] & ~R_high[7];
		end else begin
			/* ADIW */
			{nC, R_high} = GPR[24+2*Rp16+1] + C;
			nV = ~GPR[24+2*Rp16+1][7] &  R_high[7];
		end
		nN = R_high[7];
		nS = nN ^ nV;
		nZ = (R_high==0) & Z;
		pc_next = PC + 1;
		next_state = STATE_NORMAL;
		end
 
            STATE_IO_BIT: begin
                pc_next = PC + 1;
                next_state = STATE_NORMAL;
                end
 
 
	`ifdef AVR_INITIAL
	    STATE_INITIAL: begin
		if (init_count[init_depth-1] == 1'b1)
			next_state = STATE_STALL;
		else
			next_state = STATE_INITIAL;
                end
	`endif
 
	`ifdef AVR_HAVE_MUL
	    STATE_MUL: begin
 
		{ R_high, R } = product;
		if ( mul_type[0] & mul_rd[7] )
			R_high = R_high - mul_rr;
		if ( mul_type[1] & mul_rr[7] )
			R_high = R_high - mul_rd;
 
		nZ = ( {R_high, R} == 16'h0000 );
		nC = R_high[7];
 
		if ( mul_type[2] ) begin
			{ R_high, R } = { R_high[6:0], R, 1'b0 };
		end
 
		pc_next = PC + 1;
		next_state = STATE_NORMAL;
		end
	`endif
 
	endcase
 
end /* always @(*) */
 
// Note: if `interrupt` is 1, then after this point:
//  - state is STATE_NORMAL,
//  - writeback is 0,
//  - next_state is STATE_CALL.
 
`ifdef AVR_HAVE_MUL
always @(posedge clk) if (next_state==STATE_MUL) begin
	product <= GPR_Rd_mul * GPR_Rr_mul;
	mul_rd <= GPR_Rd_mul;
	mul_rr <= GPR_Rr_mul;
	mul_type <= { fmulxx, xmulsx, xmulsu };
end
`endif
 
always @(posedge clk) begin
 
	`ifdef AVR_HAVE_MUL
	if (state==STATE_MUL) begin
	/* writeback: after two-cycle MUL: */
		GPR[0] <= R;
		GPR[1] <= R_high;
	end else
	`endif
 
	`ifdef AVR_HAVE_MOVW
	/* writeback: after single cycle MOVW: */
	if (state==STATE_NORMAL && INSTR[15:8]==8'b0000_0001 && ~is_interrupt) begin
		GPR[2*RD16+0] <= R;			// GPR[2*RR16+0];
		GPR[2*RD16+1] <= R_high;		// GPR[2*RR16+1];
	end else
	`endif
 
	/* writeback: after the first and second cycle of ADIW and SUBW: */
	if (next_state==STATE_ADIW) 
		GPR[24+2*Rd16+0] <= R;
	else if (state==STATE_ADIW) 
		GPR[24+2*Rp16+1] <= R_high;
	/* writeback after LD: */
	else if ( state==STATE_LD )
		GPR[Rd_ld_save] <= dmem_di;
	else if ( state==STATE_LPM2 ) begin
		if (~lpm_z_low)	GPR[Rd_ld_save] <= INSTR[7:0];
		else		GPR[Rd_ld_save] <= INSTR[15:8];
	end else begin
 
	/* writeback: all of the another cases (ALU + X/Y/Z inc/dec): */
	case (writeback)
	    WRITEBACK_GPR: begin
		GPR[Rd] <= R;
		end
	    WRITEBACK_ZINC: begin
		GPR[30] <= RZ_inc[7:0];
		GPR[31] <= RZ_inc[15:8];
		end	
	    WRITEBACK_ZY: begin
		if (~INSTR[3]) begin
			GPR[30] <= RZ_inc_dec[7:0];
			GPR[31] <= RZ_inc_dec[15:8];
		end else begin
			GPR[28] <= RY_inc_dec[7:0]; 
			GPR[29] <= RY_inc_dec[15:8];
		end
		end
	    WRITEBACK_X: begin
		GPR[26] <= RX_inc_dec[7:0]; 
		GPR[27] <= RX_inc_dec[15:8];
		end
	endcase
 
	end
 
	{ I, T, H, S, V, N, Z, C } <= nSREG;
 
	if (~INSTR[9])	Rio <= Rin & ~(8'h01 << b);
	else		Rio <= Rin |  (8'h01 << b);
 
	pc_call <= pc_call_next;
 
	if (next_state == STATE_LD)
		Rd_ld_save <= (state == STATE_NORMAL ? Rd : Rd_prev); 
	else if (next_state == STATE_LPM) begin
		Rd_ld_save <= (INSTR[10] ? 5'b00000: Rd);
		lpm_z_low  <= RZ[0];
	end
 
	`ifdef AVR_INITIAL
	if ( ~init_count[init_depth-1] )
		init_count <= init_count + 1;
	`endif
 
	state <= next_state;
	PC <= pc_next;
 
//	if ( sp_update )
		SP <= sp_next;
 
end
 
/*****************************************************************************/
 
/* Debug section starts here */
 
wire [pmem_width:0] PC_double = {PC,1'b0};
wire [7:0] R0 = GPR[0];
wire [7:0] R1 = GPR[1];
wire [7:0] R2 = GPR[2];
wire [7:0] R3 = GPR[3];
wire [7:0] R4 = GPR[4];
wire [7:0] R5 = GPR[5];
wire [7:0] R6 = GPR[6];
wire [7:0] R7 = GPR[7];
wire [7:0] R8 = GPR[8];
wire [7:0] R9 = GPR[9];
wire [7:0] R10 = GPR[10];
wire [7:0] R11 = GPR[11];
wire [7:0] R12 = GPR[12];
wire [7:0] R13 = GPR[13];
wire [7:0] R14 = GPR[14];
wire [7:0] R15 = GPR[15];
wire [7:0] R16 = GPR[16];
wire [7:0] R17 = GPR[17];
wire [7:0] R18 = GPR[18];
wire [7:0] R19 = GPR[19];
wire [7:0] R20 = GPR[20];
wire [7:0] R21 = GPR[21];
wire [7:0] R22 = GPR[22];
wire [7:0] R23 = GPR[23];
wire [7:0] R24 = GPR[24];
wire [7:0] R25 = GPR[25];
wire [7:0] R26 = GPR[26];
wire [7:0] R27 = GPR[27];
wire [7:0] R28 = GPR[28];
wire [7:0] R29 = GPR[29];
wire [7:0] R30 = GPR[30];
wire [7:0] R31 = GPR[31];
 
`ifdef SIMULATOR
initial begin
	$dumpvars(1,PC,PC_double,INSTR,SP,SREG,state,pc_call,R);
	$dumpvars(1,R0,R1,R16,R17,R18,R19,R20,R21,R22,R23,R24,R25,RX,RY,RZ);
	$dumpvars(1,io_we,io_re,io_a,io_do,io_di,Rio);
	$dumpvars(1,dmem_we,dmem_re,dmem_a,dmem_do,dmem_di);
end
`endif
 
/* end of debug section */
/*****************************************************************************/
 
endmodule
 
/*****************************************************************************/
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.