URL
https://opencores.org/ocsvn/softavrcore/softavrcore/trunk
Subversion Repositories softavrcore
Compare Revisions
- This comparison shows the changes necessary to convert path
/softavrcore
- from Rev 1 to Rev 2
- ↔ Reverse comparison
Rev 1 → Rev 2
/trunk/build/Makefile
0,0 → 1,63
SHELL=/bin/sh |
|
CC=avr-gcc |
AS=avr-as |
AR=avr-ar |
OBJCOPY=avr-objcopy |
OBJDUMP=avr-objdump |
CHMOD=chmod |
|
ARCH = avr2 |
ARCH_LIB = crt0.o |
|
AFLAGS=-mmcu=$(ARCH) |
#CFLAGS=-mmcu=$(ARCH) -D__COMPILING_AVR_LIBC__ -Wall -pedantic -O3 |
CFLAGS=-mmcu=$(ARCH) -D__COMPILING_AVR_LIBC__ -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wno-unused-function -O3 |
LDFLAGS=-mmcu=$(ARCH) |
|
PMEM_DEPTH=10 |
PMEM_WORDS=$(shell echo $$((1<<$(PMEM_DEPTH)))) |
PMEM_SIZE=$(shell echo $$((2<<$(PMEM_DEPTH)))) |
|
PROGMEM=../util/progmem-ice40.sh |
#PROGMEM=../util/progmem-generic.sh |
|
.PHONY: all clean |
|
TARGETS=main.disasm main.mem flash_array.v main.instr main.v |
|
all: $(TARGETS) |
|
crt0.o: crt0.s |
$(AS) $(AFLAGS) -o crt0.o crt0.s |
|
main.o: main.c |
$(CC) $(CFLAGS) -c main.c |
|
main.elf: main.o $(ARCH_LIB) |
$(CC) $(LDFLAGS) -o main.elf main.o $(ARCH_LIB) |
|
main.hex: main.elf |
$(OBJCOPY) -j .text -j .data -O ihex main.elf main.hex |
|
main.bin: main.elf |
$(OBJCOPY) -j .text -j .data -O binary main.elf main.bin |
$(CHMOD) -x main.bin |
|
main.mem: main.bin |
cat main.bin /dev/zero | head -c $(PMEM_SIZE) | hexdump -v -e '/2 "%.4x\n"' > main.mem |
|
flash_array.v: main.mem |
awk '{ printf("flash_array[%d]=16%ch%s;\n",n,39,$$1);n++; }' < main.mem > flash_array.v |
|
main.disasm: main.elf |
$(OBJDUMP) -s -m $(ARCH) -d main.elf > main.disasm |
|
main.instr: main.disasm |
expand < main.disasm | grep -v "file format" | awk '{ if ( substr($$1,length($$1))==":" ) print substr($$0,25,8); }' | sort | uniq | awk '{ print $$1; }' > main.instr |
|
main.v: main.bin |
$(PROGMEM) --depth $(PMEM_DEPTH) --name flash main.bin > main.v |
|
clean: |
rm -f $(TARGETS) *.o *.elf *.mem *.disasm *.hex *.bin |
/trunk/build/crt0.s
0,0 → 1,99
;=============================================================================; |
|
.set PINB, 0x16 |
|
.set VECTOR_SIZE, 2 |
.set VECTOR_WIDTH, 2 |
|
.set VECTOR_NUM, 1<<VECTOR_WIDTH |
.set AVR_REG_SIZE, 0x0020 |
.set AVR_IO_SIZE, 0x0040 |
.set RAM_SIZE, 0x0200 |
|
;=============================================================================; |
|
.set _RAMEND_ADDR, AVR_REG_SIZE + AVR_IO_SIZE + RAM_SIZE |
.set _VECTORS_SIZE, VECTOR_SIZE * VECTOR_NUM |
.set __stack_pointer, _RAMEND_ADDR - 1 |
.set AVR_STACK_POINTER_LO_ADDR, 0x3d |
.set AVR_STACK_POINTER_HI_ADDR, 0x3e |
.set AVR_SREG_ADDR, 0x3f |
|
.macro XJMP name |
.if (VECTOR_SIZE == 2) |
rjmp \name |
.elseif (VECTOR_SIZE == 4) |
jmp \name |
.else |
nop |
.endif |
.endm |
|
.macro XCALL name |
.if (VECTOR_SIZE == 2) |
rcall \name |
.elseif (VECTOR_SIZE == 4) |
call \name |
.else |
nop |
.endif |
.endm |
|
.macro vector name |
.if (. - __vectors < _VECTORS_SIZE) |
.weak \name |
.set \name, __bad_interrupt |
XJMP \name |
.endif |
.endm |
|
.section .vectors,"ax",@progbits |
.global __vectors |
.func __vectors |
|
__vectors: |
|
;in r16, PINB |
;ldi r16, 0xC0 |
;out 0x04, r16 |
;rjmp __vectors |
|
XJMP __init |
vector __vector_1 |
vector __vector_2 |
vector __vector_3 |
vector __vector_4 |
vector __vector_5 |
vector __vector_6 |
vector __vector_7 |
vector __vector_8 |
vector __vector_9 |
vector __vector_10 |
vector __vector_11 |
vector __vector_12 |
vector __vector_13 |
vector __vector_14 |
vector __vector_15 |
.endfunc |
|
.global __bad_interrupt |
.func __bad_interrupt |
__bad_interrupt: |
.weak __vector_default |
.set __vector_default, __bad_interrupt |
rjmp __vector_default |
.endfunc |
|
__init: |
|
eor r1, r1 |
out AVR_SREG_ADDR, r1 |
ldi r28,lo8(__stack_pointer) |
out AVR_STACK_POINTER_LO_ADDR, r28 |
ldi r29,hi8(__stack_pointer) |
out AVR_STACK_POINTER_HI_ADDR, r29 |
|
.section .init9,"ax",@progbits |
|
XCALL main |
XJMP exit |
/trunk/build/main.c
0,0 → 1,195
#define F_CPU 12000000UL |
|
#include <avr/cpufunc.h> |
#include <avr/interrupt.h> |
#include <inttypes.h> |
#include <util/delay.h> |
#include <stdio.h> |
|
/*****************************************************************************/ |
|
#define __IOR(x) (*(volatile uint8_t *)(0x20+(x))) |
#define __IOW(x) (*(volatile uint16_t *)(0x20+(x))) |
|
/*****************************************************************************/ |
|
|
#define IO_BASE_UART0 0x00 |
#define IO_BASE_PORTOUT0 0x04 |
#define IO_BASE_TIMER0 0x08 |
|
/* uart.h */ |
|
#define UDR0 __IOR(IO_BASE_UART0+0x00) |
#define UCSRA0 __IOR(IO_BASE_UART0+0x01) |
#define UCSRB0 __IOR(IO_BASE_UART0+0x02) |
#define UBRR0 __IOR(IO_BASE_UART0+0x03) |
|
/* UCSRA */ |
#define RXB8 0 |
#define PE 2 |
#define DOR 3 |
#define FE 4 |
#define UDRE 5 |
#define TXC 6 |
#define RXC 7 |
|
/* UCSRB */ |
#define TXB8 0 |
#define UCSZ 1 |
#define UPM0 2 |
#define UPM1 3 |
#define USBS 4 |
#define UDRIE 5 |
#define TXCIE 6 |
#define RXCIE 7 |
|
/* timer.h */ |
|
#define TCNT0 __IOW(IO_BASE_TIMER0+0x00) |
#define TCR0 __IOR(IO_BASE_TIMER0+0x02) |
#define TSR0 __IOR(IO_BASE_TIMER0+0x03) |
|
#define TOF 7 /* timer overflow */ |
#define TOFIE 7 /* timer overflow interrupt enable */ |
#define TPRESC0 0 /* timer prescaler bit 0 */ |
#define TPRESC1 1 /* timer prescaler bit 1 */ |
|
/* port.h */ |
|
#define PORTOUT0 __IOR(IO_BASE_PORTOUT0+0x00) |
|
/*****************************************************************************/ |
|
static int uart_putchar(char c, FILE *stream) |
{ |
loop_until_bit_is_set(UCSRA0, UDRE); |
UDR0 = c; |
return(0); |
} |
|
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); |
|
/*****************************************************************************/ |
|
/* RXC */ |
ISR(_VECTOR(3)) |
{ |
uint8_t c; |
c=UDR0; |
if ( 'a' <= c && c <= 'z' ) c-=('a'-'A'); |
UDR0=c; |
} |
|
ISR(_VECTOR(1)) |
{ |
TCR0=TCR0; |
PORTOUT0 ^= 0x02; |
} |
|
|
static inline void msleep(uint16_t msec) |
{ |
while ( msec ) |
{ _delay_loop_2((uint32_t)F_CPU/4000UL); |
msec--; |
} |
} |
|
static void test_memory_buffer(void) |
{ |
static char * string="0123456789"; |
uint8_t length=10,offset,cnt; |
|
msleep(10); |
|
offset=0; |
cnt=0; |
while ( 1 ) |
{ uint8_t c; |
|
while ( ! (UCSRA0&(1<<RXC)) ); |
c=UDR0; |
|
if ( c != 10 ) |
{ c=string[offset]; |
offset++; |
if ( length==offset ) offset=0; |
} |
|
UDR0=c; |
cnt++; |
PORTOUT0=cnt&3; |
}; |
|
|
} |
|
static void test_uppercase(void) |
{ |
while ( 1 ) |
{ uint8_t c; |
while ( ! (UCSRA0&(1<<RXC)) ); |
c=UDR0; |
if ( 'a' <= c && c <= 'z' ) c-=('a'-'A'); |
while ( ! (UCSRA0&(1<<UDRE)) ); |
UDR0=c; |
}; |
|
} |
|
static void test_printf(void) |
{ |
int i; |
|
stdout=&mystdout; |
|
i=0; |
PORTOUT0 = 0x90; |
PORTOUT0 ^= 0x03; |
|
while ( 1 ) |
{ PORTOUT0 ^= 0x03; |
printf("[x] %d => %d\n",i,i*i); |
msleep(1000); |
i++; |
} |
} |
|
void test_interrupt(void) |
{ |
UCSRB0 |= (1<<RXCIE); |
|
TCR0 = 0x02; |
TCR0 |= (1<<TOFIE); |
|
sei(); |
while ( 1 ) |
{ PORTOUT0 ^= 0x01; |
msleep(500); |
} |
} |
|
|
int main(void) |
{ |
uint8_t c; |
|
UBRR0 = 13-1; |
|
test_printf(); |
test_interrupt(); |
test_uppercase(); |
test_memory_buffer(); |
|
c=0; |
while ( 1 ) |
{ msleep(500); |
PORTOUT0=c; |
UDR0='a'+c; |
c=(c+1)%4; |
} |
test_uppercase(); |
return(0); |
} |
|
/trunk/core/avr_core.v
0,0 → 1,1378
/*****************************************************************************/ |
/* 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 |
|
/*****************************************************************************/ |
/trunk/doc/diagram.txt
0,0 → 1,9
This section is still to be done... |
|
/-----\ /-----\ /-----\ /-----\ |
CLK: / \ / \ / \ / \ / |
\-----/ \-----/ \-----/ \-----/ |
|
/-------------\ /-------------\ /-------------\ /-------------\ |
< X X X X |
\-------------/ \-------------/ \-------------/ \-------------/ |
/trunk/peripherals/avr_io_out.v
0,0 → 1,42
/*****************************************************************************/ |
/* avr_io_out.v */ |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
/* (c) 2019-2020; Andras Pal <apal@szofi.net> */ |
/*****************************************************************************/ |
|
module avr_io_out |
( input clk, |
input rst, |
|
input io_re, |
input io_we, |
output [7:0] io_do, |
input [7:0] io_di, |
|
output [7:0] port |
); |
|
reg [7:0] PORT; |
|
assign port[7:0] = PORT[7:0]; |
|
assign io_do = io_re ? PORT : 8'b00000000; |
|
always @(posedge clk) begin |
|
if (io_we) begin |
PORT <= io_di; |
end |
|
end |
|
|
/*****************************************************************************/ |
/* Debug section starts here */ |
|
/* end of debug section */ |
/*****************************************************************************/ |
|
endmodule |
|
/*****************************************************************************/ |
/trunk/peripherals/avr_io_timer.v
0,0 → 1,95
/*****************************************************************************/ |
/* avr_io_timer.v */ |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
/* (c) 2019-2020; Andras Pal <apal@szofi.net> */ |
/*****************************************************************************/ |
|
module avr_io_timer |
( input clk, |
input rst, |
|
input io_re, |
input io_we, |
input [1:0] io_a, |
output [7:0] io_do, |
input [7:0] io_di, |
output irq |
); |
|
reg [15:0] TCNT; |
reg [7:0] TTMP; |
reg [7:0] TCR; |
|
reg [11:0] prescaler; |
reg [3:0] pre_prev; |
reg overflow; |
|
wire [7:0] TSR = { overflow, 7'b0000000 }; |
|
assign irq = TSR[7] & TCR[7]; |
|
/* I/O read: */ |
reg [7:0] io_do_data; |
always @(*) begin |
casex (io_a) |
2'b00: io_do_data = TCNT[7:0]; |
2'b01: io_do_data = TTMP[7:0]; |
2'b10: io_do_data = TCR; |
2'b11: io_do_data = TSR; |
endcase |
end |
assign io_do = io_re ? io_do_data : 8'b00000000; |
|
|
always @(posedge clk) begin |
|
if (io_we & ~io_re) begin |
if ( io_a==2'b01 ) TTMP <= io_di; |
if ( io_a==2'b10 ) TCR <= io_di; |
end else if ( io_re ) begin |
if ( io_a==2'b00 ) |
TTMP <= TCNT[15:8]; |
end |
|
end |
|
wire tcnt_write = io_we & (io_a==2'b00); |
wire tcr_write = io_we & (io_a==2'b10); |
|
/* Note: the interrupt is cleared when the overflow flag is reset: therefore, any write |
into the TNCT _or_ the TCR register would clear the interrupt: */ |
|
wire o = overflow & (~tcr_write); |
|
reg increment; |
|
always @(*) begin |
casex (TCR[1:0]) |
2'b00: increment = 1; |
2'b01: increment = (~prescaler[ 3])&pre_prev[0]; |
2'b10: increment = (~prescaler[ 7])&pre_prev[1]; |
2'b11: increment = (~prescaler[11])&pre_prev[2]; |
endcase |
end |
|
always @(posedge clk) begin |
if ( ! tcnt_write ) begin |
prescaler <= prescaler + 1; |
pre_prev <= { prescaler[11], prescaler[7], prescaler[3] }; |
{ overflow, TCNT } <= { o, 16'd0 } | ( { o, TCNT } + increment ); |
end else begin |
TCNT <= { TTMP, io_di }; |
prescaler <= 0; |
overflow <= 0; |
end |
end |
|
/*****************************************************************************/ |
/* Debug section starts here */ |
|
/* end of debug section */ |
/*****************************************************************************/ |
|
endmodule |
|
/*****************************************************************************/ |
/trunk/peripherals/avr_io_uart.v
0,0 → 1,244
/*****************************************************************************/ |
/* avr_io_uart.v */ |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
/* (c) 2019-2020; Andras Pal <apal@szofi.net> */ |
/*****************************************************************************/ |
|
module uart_tx (input clk, input [7:0] prescaler, input [7:0] tx_in, input strobe, output reg txd, output busy, output prefetch); |
|
parameter TX_STATE_IDLE = 0; |
parameter TX_STATE_TRANSMIT = 1; |
|
reg state = TX_STATE_IDLE; |
|
//reg txd = 1; |
|
reg [7:0] count = 0; |
reg [7:0] scaler_counter = 0; |
|
reg [7:0] dataout = 0; |
|
parameter count_step = 2; |
assign busy = state; |
|
wire scaler_limit = (scaler_counter==0); |
wire bit_limit = (count[3:0] == 4'b0000); |
wire last_bit = (count[7:4]==4'd10); |
|
assign prefetch = ( state==TX_STATE_TRANSMIT ) & scaler_limit & bit_limit & last_bit; |
|
always @(posedge clk) begin |
|
if ( state==TX_STATE_IDLE && strobe ) begin |
state <= TX_STATE_TRANSMIT; |
txd <= 0; |
count <= count_step; |
dataout <= tx_in; |
scaler_counter <= prescaler; |
end else if ( state==TX_STATE_TRANSMIT && scaler_limit ) begin |
if ( bit_limit ) begin |
if ( last_bit ) begin |
if ( strobe ) begin |
txd <= 0; |
count <= count_step; |
dataout <= tx_in; |
end else begin |
txd <= 1; |
state <= TX_STATE_IDLE; |
end |
end else begin |
txd <= dataout[0]; |
dataout <= { 1'b1, dataout[7:1] }; |
count <= count + count_step; |
end |
end else begin |
count <= count + count_step; |
end |
scaler_counter <= prescaler; |
end else if ( state==TX_STATE_TRANSMIT ) begin |
scaler_counter <= scaler_counter - 1; |
end |
|
end |
|
endmodule |
|
/*****************************************************************************/ |
|
module uart_rx (input clk, input [7:0] prescaler, input rxd, input reset, output [7:0] rx_out, output reg avail); |
|
parameter STATE_IDLE = 0; |
parameter STATE_STARTBIT = 2; |
parameter STATE_RECEIVE = 3; |
|
reg [1:0] state = STATE_IDLE; |
|
reg [7:0] count = 0; |
reg [7:0] scaler_counter = 0; |
|
reg [7:0] datain = 0; |
//reg avail = 0; |
|
parameter count_step = 2; |
|
wire rx_sub_bit = ( state==STATE_RECEIVE && scaler_counter==0 ); |
wire rx_bit = (rx_sub_bit && count[3:0] == 4'b0000); |
wire rx_completed = (rx_bit && count[7:4]==4'd9 ); |
|
assign rx_out = datain; |
|
always @(posedge clk) begin |
|
if ( state==STATE_IDLE && rxd==0 ) begin |
state <= STATE_STARTBIT; |
count <= count_step; |
scaler_counter <= prescaler; |
end else if ( state==STATE_STARTBIT && scaler_counter==0 ) begin |
if ( count[3:0] == 4'b1000 ) begin |
state <= STATE_RECEIVE; |
count <= count_step; |
end else begin |
count <= count + count_step; |
end |
scaler_counter <= prescaler; |
end else if ( state==STATE_RECEIVE && scaler_counter==0 ) begin |
if ( count[3:0] == 4'b0000 ) begin |
if ( count[7:4]==4'd9 ) begin |
state <= STATE_IDLE; |
end else begin |
datain <= { rxd, datain[7:1] }; |
count <= count + count_step; |
end |
end else begin |
count <= count + count_step; |
end |
scaler_counter <= prescaler; |
end else if ( state[1] ) begin |
scaler_counter <= scaler_counter - 1; |
end |
|
avail <= rx_completed | (avail & ~reset); |
|
end |
|
endmodule |
|
/*****************************************************************************/ |
|
module avr_io_uart |
( input clk, |
input rst, |
|
input io_re, |
input io_we, |
input [1:0] io_a, |
output [7:0] io_do, |
input [7:0] io_di, |
|
output txd, |
input rxd, |
|
output [2:0] irq |
); |
|
reg [7:0] UDR_TX = 0; |
reg [7:0] UDR_RX = 0; |
reg [7:0] UCSRB = 0; |
reg [7:0] UBRR = 0; |
|
parameter UCSRA_RXB8 = 3'd0; |
parameter UCSRA_x1 = 3'd1; |
parameter UCSRA_PE = 3'd2; |
parameter UCSRA_DOR = 3'd3; |
parameter UCSRA_FE = 3'd4; |
parameter UCSRA_UDRE = 3'd5; |
parameter UCSRA_TXC = 3'd6; |
parameter UCSRA_RXC = 3'd7; |
|
wire RXCIE, TXCIE, UDRIE, USBS, UPM1, UPM0, UCSZ, TXB8; |
assign { RXCIE, TXCIE, UDRIE, USBS, UPM1, UPM0, UCSZ, TXB8 } = UCSRB; |
|
reg rx0_non_empty = 0, rx0_overrun = 0, rx0_reset = 0; |
wire tx0_txd,tx0_busy,tx0_prefetch; |
reg tx0_non_empty = 0; |
|
wire [7:0] UCSRA = { rx0_non_empty, ~tx0_busy, ~tx0_non_empty, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0 }; |
|
assign irq = UCSRB[7:5] & UCSRA[7:5]; |
|
/* I/O read: */ |
reg [7:0] io_do_data; |
always @(*) begin |
casex (io_a) |
2'b00: io_do_data = UDR_RX[7:0]; |
2'b01: io_do_data = UCSRA; |
2'b10: io_do_data = UCSRB; |
2'b11: io_do_data = UBRR; |
endcase |
end |
assign io_do = io_re ? io_do_data : 8'b00000000; |
|
/* I/O write: configuration: */ |
always @(posedge clk) begin |
if ( io_we ) begin |
casex (io_a) |
2'b10: UCSRB <= io_di; |
2'b11: UBRR <= io_di; |
endcase |
end |
end |
|
/* TX */ |
|
|
uart_tx tx0 (clk, UBRR, UDR_TX, tx0_non_empty, tx0_txd, tx0_busy, tx0_prefetch); |
|
/* transmitter state changes: */ |
always @(posedge clk) begin |
if ( io_we && io_a == 2'b00 && ~tx0_non_empty ) begin |
tx0_non_empty <= 1; |
UDR_TX <= io_di; |
end else if ( (tx0_non_empty & ~tx0_busy) | tx0_prefetch ) |
tx0_non_empty <= 0; |
end |
|
assign txd = tx0_txd | (~tx0_busy); |
|
/* RX */ |
|
wire [7:0] rx0_data; |
wire rx0_avail; |
|
uart_rx rx0 (clk, UBRR, rxd, rx0_reset, rx0_data, rx0_avail); |
|
/* receiver state changes: */ |
always @(posedge clk) begin |
if ( io_re && io_a == 2'b00 ) begin |
rx0_non_empty <= 0; |
rx0_overrun <= 0; |
end else if ( rx0_avail && ~rx0_reset ) begin |
UDR_RX <= rx0_data; |
rx0_non_empty <= 1; |
rx0_overrun <= rx0_non_empty; |
rx0_reset <= 1; |
end else begin |
rx0_reset <= 0; |
end |
end |
|
/*****************************************************************************/ |
/* Debug section starts here */ |
|
`ifdef SIMULATOR |
initial begin |
$dumpvars(1,UDR_TX,UDR_RX,UCSRB,UBRR,tx0_non_empty,tx0_busy,tx0_prefetch); |
$dumpvars(1,rxd,rx0_non_empty,rx0_avail,rx0_data,rx0_reset,rx0_overrun); |
end |
`endif |
|
/* end of debug section */ |
/*****************************************************************************/ |
|
endmodule |
|
/*****************************************************************************/ |
/trunk/peripherals/avr_systick.v
0,0 → 1,80
/*****************************************************************************/ |
/* avr_systick.v */ |
/*****************************************************************************/ |
/* Registers */ |
/* STCNTL r BASE + 0x00 { CNT[7:0] } */ |
/* STCNTH r BASE + 0x01 { OVERFLOW, CNT[14:8] } */ |
/* STLOADL r+w BASE + 0x02 { CLOAD[7:0] } */ |
/* STLOADH r+w BASE + 0x03 { IENABLE, CLOAD[14:8] } */ |
/*****************************************************************************/ |
|
module avr_systick |
( input clk, |
input rst, |
|
input io_re, |
input io_we, |
input [1:0] io_a, |
output [7:0] io_do, |
input [7:0] io_di, |
output irq |
); |
|
reg IENABLE; |
reg CLOAD[14:0]; |
reg OVERFLOW; |
reg [14:0] CNT; |
reg [6:0] CTMP; |
|
assign irq = IENABLE & OVERFLOW; |
|
/* I/O read: */ |
reg [7:0] io_do_data; |
always @(*) begin |
casex (io_a) |
2'b00: io_do_data = CNT[7:0]; |
2'b01: io_do_data = { OVERFLOW, CTMP }; |
2'b10: io_do_data = CLOAD[7:0]; |
2'b11: io_do_data = { IENABLE, CLOAD[14:8] } ; |
endcase |
end |
|
assign io_do = io_re ? io_do_data : 8'b00000000; |
|
wire reset_overflow_bit = (io_we & (io_a[1]==0)); |
|
always @(posedge clk) begin |
|
if (io_we & ~io_re) begin |
if ( io_a==2'b10 ) begin |
CLOAD[7:0] <= io_di; |
end else if ( io_a==2'b11 ) begin |
{ IENABLE, CLOAD[14:8] } <= io_di; |
end |
end else if ( io_re ) begin |
if ( io_a==2'b00 ) |
CTMP <= CNT[15:8]; |
end |
|
end |
|
always @(posedge clk) begin |
if ( reset_overflow_bit ) begin |
OVERFLOW <= 0; |
end else if ( CNT[14:0]==0 ) begin |
CNT <= CLOAD; |
OVERFLOW <= 1; |
end else begin |
CNT <= CNT - 1; |
end |
end |
|
/*****************************************************************************/ |
/* Debug section starts here */ |
|
/* end of debug section */ |
/*****************************************************************************/ |
|
endmodule |
|
/*****************************************************************************/ |
/trunk/synth/Makefile
0,0 → 1,27
SHELL=/bin/bash |
|
SYNTH=yosys |
PNR=nextpnr-ice40 |
PACK=icepack |
|
DEVICE=hx8k |
#PACKAGE=bg121 |
PACKAGE=ct256 |
|
.PHONY: all clean |
|
TOP=top |
|
all: $(TOP).bin |
|
$(TOP).json: $(TOP).v |
$(SYNTH) -q -p 'synth_ice40 -top $(TOP) -json $(TOP).json' $(TOP).v |
|
$(TOP).asc: $(TOP).json $(TOP).pcf |
$(PNR) --$(DEVICE) --package $(PACKAGE) --json $(TOP).json --pcf $(TOP).pcf --seed 1 --randomize-seed --asc $(TOP).asc |
|
$(TOP).bin: $(TOP).asc |
$(PACK) $(TOP).asc $(TOP).bin |
|
clean: |
rm -f $(TOP).bin $(TOP).asc $(TOP).json |
/trunk/synth/avr_core.v
0,0 → 1,27
link ../core/avr_core.v |
trunk/synth/avr_core.v
Property changes :
Added: svn:special
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/synth/avr_io_out.v
===================================================================
--- trunk/synth/avr_io_out.v (nonexistent)
+++ trunk/synth/avr_io_out.v (revision 2)
@@ -0,0 +1 @@
+link ../peripherals/avr_io_out.v
\ No newline at end of file
trunk/synth/avr_io_out.v
Property changes :
Added: svn:special
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/synth/avr_io_timer.v
===================================================================
--- trunk/synth/avr_io_timer.v (nonexistent)
+++ trunk/synth/avr_io_timer.v (revision 2)
@@ -0,0 +1 @@
+link ../peripherals/avr_io_timer.v
\ No newline at end of file
trunk/synth/avr_io_timer.v
Property changes :
Added: svn:special
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/synth/avr_io_uart.v
===================================================================
--- trunk/synth/avr_io_uart.v (nonexistent)
+++ trunk/synth/avr_io_uart.v (revision 2)
@@ -0,0 +1 @@
+link ../peripherals/avr_io_uart.v
\ No newline at end of file
trunk/synth/avr_io_uart.v
Property changes :
Added: svn:special
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/synth/flash.v
===================================================================
--- trunk/synth/flash.v (nonexistent)
+++ trunk/synth/flash.v (revision 2)
@@ -0,0 +1,26 @@
+module flash
+ #( parameter flash_file = "main.mem",
+ parameter flash_width = 10
+ )
+ ( input clk,
+ input mem_ce,
+ input [flash_width-1:0] mem_a,
+ output [15:0] mem_d
+ );
+
+reg [15:0] flash_array [0:2**flash_width-1];
+
+reg [15:0] data_read;
+
+assign mem_d = data_read;
+
+always @(posedge clk) begin
+ if (mem_ce) data_read <= flash_array[mem_a];
+end
+
+initial begin
+// $readmemh(flash_file, flash_array);
+ `include "flash_array.v"
+end
+
+endmodule
Index: trunk/synth/flash_array.v
===================================================================
--- trunk/synth/flash_array.v (nonexistent)
+++ trunk/synth/flash_array.v (revision 2)
@@ -0,0 +1 @@
+link ../build/flash_array.v
\ No newline at end of file
trunk/synth/flash_array.v
Property changes :
Added: svn:special
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/synth/main.mem
===================================================================
--- trunk/synth/main.mem (nonexistent)
+++ trunk/synth/main.mem (revision 2)
@@ -0,0 +1 @@
+link ../build/main.mem
\ No newline at end of file
trunk/synth/main.mem
Property changes :
Added: svn:special
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/synth/main.v
===================================================================
--- trunk/synth/main.v (nonexistent)
+++ trunk/synth/main.v (revision 2)
@@ -0,0 +1 @@
+link ../build/main.v
\ No newline at end of file
trunk/synth/main.v
Property changes :
Added: svn:special
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/synth/ram.v
===================================================================
--- trunk/synth/ram.v (nonexistent)
+++ trunk/synth/ram.v (revision 2)
@@ -0,0 +1,25 @@
+module ram
+ #( parameter ram_width = 9
+ )
+ ( input clk,
+ input re,
+ input we,
+ input [ram_width-1:0] addr,
+ output [7:0] data_read,
+ input [7:0] data_write
+ );
+
+reg [7:0] ram_array [0:2**ram_width-1];
+reg [7:0] data_out;
+
+assign data_read = data_out;
+
+always @(posedge clk) begin
+ if (we) ram_array[addr] <= data_write;
+end
+
+always @(posedge clk) begin
+ if (re) data_out <= ram_array[addr];
+end
+
+endmodule
Index: trunk/synth/top-bg121.pcf
===================================================================
--- trunk/synth/top-bg121.pcf (nonexistent)
+++ trunk/synth/top-bg121.pcf (revision 2)
@@ -0,0 +1,25 @@
+# GRBAlpha ballon payload board
+
+set_io led[1] A4
+set_io led[0] B4
+
+set_io hwclk L5
+
+# This is the input for the FPGA, signal comes from the FT2232:
+set_io ftdi_rx K7
+# This is the output for the FPGA, signal goes to the FT2232:
+set_io ftdi_tx L7
+
+set_io sseg4[12] B2 # DD
+set_io sseg4[11] D2 # D1
+set_io sseg4[10] C2 # D2
+set_io sseg4[9] A2 # D3
+set_io sseg4[8] B3 # D4
+set_io sseg4[7] G2 # a
+set_io sseg4[6] E2 # b
+set_io sseg4[5] H1 # c
+set_io sseg4[4] E1 # d
+set_io sseg4[3] F1 # e
+set_io sseg4[2] F2 # f
+set_io sseg4[1] D1 # g
+set_io sseg4[0] G1 # dp
Index: trunk/synth/top-ct256.pcf
===================================================================
--- trunk/synth/top-ct256.pcf (nonexistent)
+++ trunk/synth/top-ct256.pcf (revision 2)
@@ -0,0 +1,35 @@
+# iCE40HX8K-CT256 ICE40HX8K-B-EVN
+
+set_io led[7] B5
+set_io led[6] B4
+set_io led[5] A2
+set_io led[4] A1
+set_io led[3] C5
+set_io led[2] C4
+set_io led[1] B3
+set_io led[0] C3
+
+set_io hwclk J3
+
+# This is the input for the FPGA top module:
+set_io ftdi_rx B10 # input
+# This is the output for the FPGA top module:
+set_io ftdi_tx B12 # output
+
+set_io pin_scl0 D16
+set_io pin_sda0 C16
+
+# Another 5 ports connected to the FTDI transceiver:
+#set_io ftdi_nrts B13 # input
+#set_io ftdi_ncts A15 # output
+#set_io ftdi_ndtr A16 # input
+#set_io ftdi_ndsr B14 # output
+#set_io ftdi_ndcd B15 # output
+#set_io ftdi_rx B9 # orange
+#set_io ftdi_tx A7 # yellow
+#set_io nss B8
+#set_io sck A9
+#set_io miso A10
+#set_io mosi A11
+
+
Index: trunk/synth/top-digilent_nexys_a7-cx7a100t.xdc
===================================================================
--- trunk/synth/top-digilent_nexys_a7-cx7a100t.xdc (nonexistent)
+++ trunk/synth/top-digilent_nexys_a7-cx7a100t.xdc (revision 2)
@@ -0,0 +1,11 @@
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN E3 } [get_ports hwclk]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN H17 } [get_ports {led[0]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN K15 } [get_ports {led[1]}]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN C4 } [get_ports ftdi_rx]
+set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN D4 } [get_ports ftdi_tx]
+
+create_clock -period 10 [get_ports hwclk]
+
+set_property CONFIG_VOLTAGE 3.3 [current_design]
+set_property CFGBVS VCCO [current_design]
+
Index: trunk/synth/top.pcf
===================================================================
--- trunk/synth/top.pcf (nonexistent)
+++ trunk/synth/top.pcf (revision 2)
@@ -0,0 +1 @@
+link top-ct256.pcf
\ No newline at end of file
trunk/synth/top.pcf
Property changes :
Added: svn:special
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/synth/top.v
===================================================================
--- trunk/synth/top.v (nonexistent)
+++ trunk/synth/top.v (revision 2)
@@ -0,0 +1,237 @@
+`include "avr_core.v"
+`include "avr_io_out.v"
+`include "avr_io_uart.v"
+`include "avr_io_timer.v"
+`include "main.v"
+//`include "flash.v"
+`include "ram.v"
+
+//`include "avr_io_spi.v"
+
+/*****************************************************************************/
+
+module priority_encoder ( input [3:0] irq_lines , output iflag, output reg [1:0] ivect );
+
+//reg [1:0] ivect;
+
+always @(*) begin
+ if (irq_lines[0]) ivect = 0;
+ else if (irq_lines[1]) ivect = 1;
+ else if (irq_lines[2]) ivect = 2;
+ else if (irq_lines[3]) ivect = 3;
+ else ivect = 0;
+end
+
+assign iflag = |irq_lines;
+
+endmodule
+
+/*****************************************************************************/
+
+module top
+ ( input hwclk,
+ output [7:0] led,
+ input ftdi_rx,
+ output ftdi_tx,
+ inout pin_scl0,
+ inout pin_sda0
+
+ );
+
+//assign sseg4 = 13'b1_1111_1111_1111;
+
+wire clk;
+
+parameter pmem_width = 10;
+parameter dmem_width = 9;
+
+wire pmem_ce;
+wire [pmem_width-1:0] pmem_a;
+wire [15:0] pmem_d;
+
+wire dmem_re;
+wire dmem_we;
+wire [dmem_width-1:0] dmem_a;
+wire [7:0] dmem_di;
+wire [7:0] dmem_do;
+
+wire io_re;
+wire io_we;
+wire [5:0] io_a;
+wire [7:0] io_do;
+
+
+SB_PLL40_CORE
+ #( .FEEDBACK_PATH("SIMPLE"),
+ .PLLOUT_SELECT("GENCLK"),
+ .ENABLE_ICEGATE("0"),
+ .DIVR(4'b0000),
+ .DIVF(7'b0111111),
+ .DIVQ(3'b100),
+ .FILTER_RANGE(3'b001)
+ )
+pll
+ ( .RESETB(1'b1),
+ .BYPASS(1'b1),
+ .EXTFEEDBACK(1'b0),
+ .LATCHINPUTVALUE(1'b0),
+ .DYNAMICDELAY(8'b00000000),
+ .REFERENCECLK(hwclk),
+ .SDI(1'b0),
+ .SCLK(1'b0),
+ .PLLOUTGLOBAL(clk)
+ );
+
+//reg [1:0] clkcnt;
+//always @(posedge hwclk) clkcnt <= clkcnt + 1;
+//assign clk = clkcnt[1];
+//BUFG clkcrt ( .I(clkcnt[1]), .O(clk) );
+
+/*****************************************************************************/
+
+ram core0_ram ( clk, dmem_re, dmem_we, dmem_a, dmem_di, dmem_do );
+defparam core0_ram.ram_width = dmem_width;
+
+flash core0_flash ( clk, pmem_ce,pmem_a, pmem_d );
+//defparam core0_flash.flash_width = pmem_width;
+
+/*****************************************************************************/
+
+wor [7:0] io_di;
+
+`define TIMER0
+
+`ifdef TIMER0
+wire timer0_io_select = (io_a[5:2] == 4'b0010);
+wire timer0_io_re = timer0_io_select & io_re;
+wire timer0_io_we = timer0_io_select & io_we;
+wire timer0_irq;
+
+avr_io_timer timer0
+ ( clk, 1'b0,
+ timer0_io_re, timer0_io_we, io_a[1:0], io_di, io_do,
+ timer0_irq
+ );
+`else
+wire timer0_irq = 0;
+`endif
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+`define PORT0
+
+`ifdef PORT0
+wire port0_io_select = (io_a[5:0] == 6'b000100);
+wire port0_io_re = (port0_io_select ? io_re : 1'b0);
+wire port0_io_we = (port0_io_select ? io_we : 1'b0);
+wire [7:0] port0_out;
+
+avr_io_out port0
+ ( clk, 1'b0,
+ port0_io_re, port0_io_we, io_di, io_do,
+ port0_out
+ );
+
+assign led = port0_out;
+`else
+assign led = 8'b00000000;
+`endif
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+`define UART0
+
+`ifdef UART0
+wire uart0_io_select = (io_a[5:2] == 4'b0000);
+wire uart0_io_re = (uart0_io_select ? io_re : 1'b0);
+wire uart0_io_we = (uart0_io_select ? io_we : 1'b0);
+wire uart0_txd;
+wire uart0_rxd;
+wire [2:0] uart0_irq;
+
+assign ftdi_tx = uart0_txd;
+assign uart0_rxd = ftdi_rx;
+
+avr_io_uart uart0
+ ( clk, 1'b0,
+ uart0_io_re, uart0_io_we, io_a[1:0], io_di, io_do,
+ uart0_txd, uart0_rxd,
+ uart0_irq
+ );
+`else
+assign ftdi_tx = ftdi_rx;
+wire [2:0] uart0_irq;
+assign uart0_irq = 3'b000;
+`endif
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+// `define SPI0
+
+`ifdef SPI0
+wire spi0_io_select = (io_a[5:2] == 4'b0100);
+wire spi0_io_re = (spi0_io_select ? io_re : 1'b0);
+wire spi0_io_we = (spi0_io_select ? io_we : 1'b0);
+wire spi0_enable;
+wire spi0_master;
+wire spi0_master_out;
+wire spi0_master_in;
+wire spi0_master_clk;
+wire spi0_master_select;
+wire spi0_slave_out;
+
+`define ASYNC_SPI_SAMPLING
+
+`ifdef ASYNC_SPI_SAMPLING
+assign mosi = spi0_master_out;
+assign spi0_master_in = miso;
+assign sck = spi0_master_clk;
+assign nss = ~spi0_master_select;
+`else
+reg mosi, spi0_master_in, sck, nss;
+always @(posedge clk) begin
+ mosi <= spi0_master_out;
+ spi0_master_in <= miso;
+ sck <= spi0_master_clk;
+ nss <= ~spi0_master_select;
+end
+`endif
+
+avr_io_spi spi0
+ ( clk, 1'b0,
+ spi0_io_re, spi0_io_we, io_a[1:0], io_di, io_do,
+ spi0_enable, spi0_master,
+ spi0_master_clk, spi0_master_out, spi0_master_in, spi0_master_select,
+ 1'b0, 1'b0, spi0_slave_out, 1'b0
+ );
+`else
+
+//assign sck = 1'b0;
+//assign mosi = 1'b0;
+//assign nss = 1'b1;
+
+`endif
+
+
+/*****************************************************************************/
+
+wire iflag;
+wire [1:0] ivect;
+
+priority_encoder irq0 ( { uart0_irq[2], 1'b0, timer0_irq, 1'b0 }, iflag, ivect );
+
+avr_core core0
+ ( clk, 1'b0,
+ pmem_ce, pmem_a, pmem_d,
+ dmem_re, dmem_we, dmem_a, dmem_di, dmem_do,
+ io_re, io_we, io_a, io_di, io_do,
+ iflag, ivect
+ );
+
+defparam core0.pmem_width = pmem_width;
+defparam core0.dmem_width = dmem_width;
+defparam core0.interrupt = 1;
+defparam core0.intr_width = 2;
+
+endmodule
+
Index: trunk/util/progmem-generic.sh
===================================================================
--- trunk/util/progmem-generic.sh (nonexistent)
+++ trunk/util/progmem-generic.sh (revision 2)
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+infile=""
+
+depth=8
+
+name=rom_16bit
+
+while [ -n "$1" ]; do
+ case "$1" in
+ -h|--help)
+ echo -e "Usage:\t$0 [-h|--help] [-w|--width ] <16-bit-image.bin>"
+ exit 0
+ ;;
+ -w|--width|-d|--depth)
+ depth="$2"
+ shift
+ ;;
+ -n|--name)
+ name="$2"
+ shift
+ ;;
+ -*)
+ echo -e "$0: error: invalid command line argument near '$1'." >> /dev/stderr
+ exit 1
+ ;;
+ *)
+ infile="$1"
+ ;;
+ esac; shift
+done
+
+if ! [ -n "$infile" ] || ! [ -f "$infile" ]; then
+ echo "$0: error: input file is missing or not found." >> /dev/stderr
+ exit 1
+fi
+
+nword=$((1<
trunk/util/progmem-generic.sh
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/util/progmem-ice40.sh
===================================================================
--- trunk/util/progmem-ice40.sh (nonexistent)
+++ trunk/util/progmem-ice40.sh (revision 2)
@@ -0,0 +1,115 @@
+#!/bin/bash
+
+infile=""
+
+depth=8
+
+name=rom_16bit
+
+while [ -n "$1" ]; do
+ case "$1" in
+ -h|--help)
+ echo -e "Usage:\t$0 [-h|--help] [-w|--width ] <16-bit-image.bin>"
+ exit 0
+ ;;
+ -w|--width|-d|--depth)
+ depth="$2"
+ shift
+ ;;
+ -n|--name)
+ name="$2"
+ shift
+ ;;
+ -*)
+ echo -e "$0: error: invalid command line argument near '$1'." >> /dev/stderr
+ exit 1
+ ;;
+ *)
+ infile="$1"
+ ;;
+ esac; shift
+done
+
+if ! [ -n "$infile" ] || ! [ -f "$infile" ]; then
+ echo "$0: error: input file is missing or not found." >> /dev/stderr
+ exit 1
+fi
+
+nword=$((1<
trunk/util/progmem-ice40.sh
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/Makefile
===================================================================
--- trunk/Makefile (nonexistent)
+++ trunk/Makefile (revision 2)
@@ -0,0 +1,12 @@
+SHELL=/bin/sh
+
+.PHONY: all clean
+
+all:
+ $(MAKE) -C build
+ $(MAKE) -C synth
+
+clean:
+ $(MAKE) -C build clean
+ $(MAKE) -C synth clean
+
Index: trunk/README
===================================================================
--- trunk/README (nonexistent)
+++ trunk/README (revision 2)
@@ -0,0 +1,121 @@
+Soft AVR Core + Interfaces
+==========================
+
+Introduction
+------------
+
+This package is a full-stack implementation of the AVR 2-stage pipeline,
+featuring synthesis for AVR2 (classic core), AVR2.5 (classic plus), AVR3
+(with extended program memory), AVR4 (enhanced core) and AVR5 (enhanced core
+with extended program memory). Interrupts are supported with customized number
+of IRQ vector width.
+
+The project comes with some example peripherals, such as UART, SPI, a
+basic timer, output port and SysTick timer.
+
+Synthetized and tested using various tools, including free & open source
+packages:
+ - iCE40HX8K-BG121 and iCE40HX8K-CT256 (on ICE40HX8K-B-EVN and custom
+design boards): Project IceStorm: yosys-0.9, nextpnr-ice40 and icestorm
+utilities;
+ - iCE40HX8K-BG121 and iCE40HX8K-CT256 (on ICE40HX8K-B-EVN and custom
+design boards): Lattice iCEcube2; and
+ - XA7A100T-1CSG324 (on a Digilent Nexys A7 board): Xilinx Vivado 2019.1.
+
+Software run by the core can seamlessly be built with the AVR-GCC
+toolchain. This bundle includes utilities aiding the conversion from ELF
+output to BRAM initializations (designed for iCE40 EBRs) or generic
+synchronous ROM interface to set up the initial program memory. A
+configurable startup code (crt0.s) is included in the package with
+options to be matched to the synthetized core architecture.
+
+Availability
+------------
+
+This package is available from https://szofi.net/pub/verilog/softavrcore/.
+Select the softavrcore-latest.tar.gz file for the latest version. Comments
+are welcomed! Contact: Andras Pal .
+
+Getting started
+---------------
+
+ - On a Linux system, install the following toolchains and utilities:
+ * gcc-avr
+ * avr-libc
+ * binutils-avr
+ * icestorm
+ * yosys
+ * nextpnr-ice40
+ then enter `make` in the main directory. This will compile the example
+ C code (found in ./build) and then run the synthesis and place-and-route
+ targeted for the ICE40HX8K-B-EVN board. This step is automatically following
+ by the generation of the FPGA configuration bitstream for iCE40HX8K-CT256
+ in the file top.bin.
+
+ - On another operating systems for non-Lattice FPGA targets:
+ * use the corresponding AVR port to compile the source and create
+ the main.bin file. A working bash/awk is needed to automatically
+ create the *.v files containing the flash interface for this
+ virtual MCU. These are available on MacOS by default. On Windows, you
+ may need to install additional components (e.g. Cygwin).
+ * Collect the source *.v files, including the core (avr_core.v),
+ peripherals (avr_io_*.v), flash interface (main.v), data memory
+ (ram.v) and the top module (top.v) into a single directory _if_ your
+ operating system does not support symlinks.
+ * Import the top.v to your synthesis toolchain (icecube2, vivado, ...).
+ You can use the shipped *.pcf files for Lattice tools (such as
+ icecube2) without any further modifications. For Xilinx, you may
+ use the file top-digilent_nexys_a7-cx7a100t.xdc as a starting point,
+ or use it without any modifications for the Digilent Nexys A7 board.
+
+By default the example code (./build/main.c) sends the following series of
+messages via the built-in secondary UART interface of the ICE40HX8K-B-EVN
+board at 115200 baud:
+ [x] 0 => 0
+ [x] 1 => 1
+ [x] 2 => 4
+ [x] 3 => 9
+ [x] 4 => 16
+ [x] 5 => 25
+ [x] 6 => 36
+ ...
+Here the cadence is one message per minute. The cores and the C code expect
+a 12MHz clock input for baud rate configuration and during the computation
+of the timer delay.
+
+You may change the contents of the main() function to switch to another
+examples. Note also that the example is fitted for 1024 words of program code
+(i.e. 2048 bytes of program flash memory). Change top.v and ./build/Makefile
+accordingly for larger (or smaller) program memory configurations.
+
+Known issues
+------------
+
+ - LD/ST operations work only on data memory interface, not on the I/O
+port and the register file. GCC is not known to generate such code unless
+register mappings are explicitly indexed with the X, Y or Z pointer registers.
+Since registers are not available directly for C code and I/O ports are
+defined to be constants for all of the relevant peripherals, it is not
+expected at all and access to those areas are seamlessly translated by GCC to
+the faster MOV, IN and OUT instructions instead of LD/ST.
+ - SPM instruction is not supported, however, equivalent
+self-programming interfaces can be synthetized by custom peripherals.
+ - Watchdog is not supported, however, equivalent functionality can be
+synthetized by custom peripherals.
+ - Automatic interrupt acknowledgement is not supported at the moment.
+ - Fuse bits and in-system programming are not supported. These are, in
+practice, nearly meaningless on such an FPGA-based CPU/MCU implementation.
+ - This soft AVR CPU is cycle compatible with the exception of the store
+operations (LD, LDS, LDD, PUSH). These store operations runs faster by 1 cycle
+compared to the AVR hardware. Use a preceeding or following NOP to be
+cycle compatible with off-the-shelf AVR hardware.
+
+Coming soon
+-----------
+
+ - I2C peripheral
+ - CAN bus interface
+ - An implementation of the AVR architecture using a 4-stage pipeline
+ - FreeRTOS port
+ - some more detaild documentation
+
© copyright 1999-2024
OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.