1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-25 00:32:27 +00:00

updated verilog presets and test makefile

This commit is contained in:
Steven Hugg 2018-02-15 12:31:32 -06:00
parent 56ed79c14f
commit 1790ca1747
29 changed files with 1002 additions and 555 deletions

View File

@ -48,7 +48,7 @@ module segments_to_bitmap(segments, line, bits);
endmodule
module test_numbers_top(clk, reset, hsync, vsync, rgb);
module test_7segment_top(clk, reset, hsync, vsync, rgb);
input clk, reset;
output hsync, vsync;

7
presets/verilog/Makefile Normal file
View File

@ -0,0 +1,7 @@
check:
verilator --top-module frame_buffer_top --lint-only *.v
iverilog -tnull *.v
deps.dot:
grep \`include *.v | sed "s/:/ /g" | awk '{ print "\"" $1 "\" -> " $3 ";" }'

View File

@ -1,6 +1,6 @@
`include "hvsync_generator.v"
module ball_paddle_top(clk, reset, hsync, vsync, rgb);
module ball_absolute_top(clk, reset, hsync, vsync, rgb);
input clk;
input reset;

View File

@ -21,7 +21,7 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
reg ball_speed_x;
reg ball_dir_y;
reg brick_array [BRICKS_H * BRICKS_V];
reg brick_array [0:BRICKS_H * BRICKS_V];
wire [3:0] score0;
wire [3:0] score1;

View File

@ -1,6 +1,6 @@
`include "hvsync_generator.v"
module ball_paddle_top(clk, reset, hsync, vsync, rgb);
module ball_slip_counter_top(clk, reset, hsync, vsync, rgb);
input clk;
input reset;

175
presets/verilog/cpu16.v Normal file
View File

@ -0,0 +1,175 @@
`ifndef CPU16_H
`define CPU16_H
`include "cpu8.v"
/*
00000aaa 0++++bbb operation A+B->A
00001ttt ######## conditional branch
00100aaa ######## load constant
00101aaa ######## load memory
00110aaa ######## store memory
01000aaa #####bbb load [B+#] -> A
01010aaa #####bbb store A -> [B+#]
10+++aaa 10+++aaa dual unary operation
11+++aaa ######## immediate binary operation
*/
module CPU16(clk, reset, address, data_in, data_out, write);
input clk;
input reset;
output [15:0] address;
input [15:0] data_in;
output [15:0] data_out;
output write;
reg [15:0] regs[0:7];
reg [2:0] state;
reg carry;
reg zero;
reg neg;
wire [2:0] flags = { neg, zero, carry };
reg [15:0] opcode;
wire [16:0] Y;
reg [3:0] aluop;
wire [2:0] rdest = opcode[10:8];
wire [2:0] rsrc = opcode[2:0];
wire Bload = opcode[11]; // TODO
localparam S_RESET = 0;
localparam S_SELECT = 1;
localparam S_DECODE = 2;
localparam S_COMPUTE = 3;
localparam IP = 7; // IP = register 7
ALU #(16) alu(
.A(regs[rdest]),
.B(regs[rsrc]),
.Y(Y),
.aluop(aluop),
.carry(carry));
always @(posedge clk)
if (reset) begin
state <= 0;
write <= 0;
end else begin
case (state)
// state 0: reset
S_RESET: begin
regs[IP] <= 16'h8000;
write <= 0;
state <= S_SELECT;
end
// state 1: select opcode address
S_SELECT: begin
address <= regs[IP];
regs[IP] <= regs[IP] + 1;
write <= 0;
state <= S_DECODE;
end
// state 2: read/decode opcode
S_DECODE: begin
opcode <= data_in; // (only use opcode next cycle)
casez (data_in)
// 00000aaa 0++++bbb operation A+B->A
16'b00000???0???????: begin
aluop <= data_in[6:3];
state <= S_COMPUTE;
end
// 00100aaa ######## load constant
16'b00100???????????: begin
regs[rdest] <= {8'b0, data_in[7:0]};
state <= S_SELECT;
end
// 00101aaa ######## load memory
16'b00101???????????: begin
address <= {8'b0, data_in[7:0]};
aluop <= `OP_LOAD_B;
state <= S_COMPUTE;
end
// 00110aaa ######## store memory
16'b00110???????????: begin
address <= {8'b0, data_in[7:0]};
data_out <= regs[data_in[10:8]];
write <= 1;
state <= S_SELECT;
end
// 00001ttt ######## conditional branch
16'b00001???????????: begin
if (
(data_in[8] && (data_in[10] == carry)) ||
(data_in[9] && (data_in[10] == zero)))
begin
// relative branch, sign extended
regs[IP] <= regs[IP] + { {8{data_in[7]}}, data_in[7:0]};
end
state <= S_SELECT;
end
// fall-through RESET
default: begin
state <= S_RESET; // reset
end
endcase
end
// state 3: compute ALU op and flags
S_COMPUTE: begin
// transfer ALU output to destination
regs[rdest] <= Y[15:0];
// set carry for certain operations (4-7,12-15)
if (aluop[2]) carry <= Y[16];
// set zero flag
zero <= ~|Y[15:0];
neg <= Y[15];
// repeat CPU loop
state <= S_SELECT;
end
endcase
end
endmodule
module test_CPU16_top(
input clk,
input reset,
output [15:0] address_bus,
output reg [15:0] to_cpu,
output [15:0] from_cpu,
output write_enable,
output [15:0] IP,
output zero,
output carry
);
reg [15:0] ram[0:65535];
reg [15:0] rom[0:65535];
assign IP = cpu.regs[7];
assign zero = cpu.zero;
assign carry = cpu.carry;
CPU16 cpu(.clk(clk),
.reset(reset),
.address(address_bus),
.data_in(to_cpu),
.data_out(from_cpu),
.write(write_enable));
always @(posedge clk)
if (write_enable) begin
ram[address_bus] <= from_cpu;
end
always @(*)
if (address_bus[15] == 0)
to_cpu = ram[address_bus];
else
to_cpu = rom[address_bus];
endmodule
`endif

View File

@ -1,52 +1,56 @@
`ifndef ALU_H
`define ALU_H
// ALU operations
parameter OP_LOAD_A = 4'h0;
parameter OP_LOAD_B = 4'h1;
parameter OP_INC = 4'h2;
parameter OP_DEC = 4'h3;
parameter OP_OR = 4'h4;
parameter OP_AND = 4'h5;
parameter OP_XOR = 4'h6;
parameter OP_ZERO = 4'h7;
// operations that generate carry
parameter OP_ADD = 4'h8;
parameter OP_SUB = 4'h9;
parameter OP_ASL = 4'ha;
parameter OP_LSR = 4'hb;
// operations that generate and use carry
parameter OP_ADC = 4'hc;
parameter OP_SBB = 4'hd;
parameter OP_ROL = 4'he;
parameter OP_ROR = 4'hf;
`define OP_LOAD_A 4'h0
`define OP_LOAD_B 4'h1
`define OP_INC 4'h2
`define OP_DEC 4'h3
`define OP_ASL 4'h4
`define OP_LSR 4'h5
`define OP_ROL 4'h6
`define OP_ROR 4'h7
`define OP_OR 4'h8
`define OP_AND 4'h9
`define OP_XOR 4'ha
`define OP_ZERO 4'hb
`define OP_ADD 4'hc
`define OP_SUB 4'hd
`define OP_ADC 4'he
`define OP_SBB 4'hf
module ALU(A, B, Y, aluop, carry);
input [7:0] A;
input [7:0] B;
output [8:0] Y;
parameter N = 8;
input [N-1:0] A;
input [N-1:0] B;
output [N:0] Y;
input [3:0] aluop;
input carry;
always @(*)
case (aluop)
OP_LOAD_A: Y = {1'b0, A};
OP_LOAD_B: Y = {1'b0, B};
OP_INC: Y = A + 1;
OP_DEC: Y = A - 1;
OP_OR: Y = {1'b0, A | B};
OP_AND: Y = {1'b0, A & B};
OP_XOR: Y = {1'b0, A ^ B};
OP_ZERO: Y = 0;
OP_ADD: Y = A + B;
OP_SUB: Y = A - B;
OP_ASL: Y = {A, 1'b0};
OP_LSR: Y = {A[0], 1'b0, A[7:1]};
OP_ADC: Y = A + B + (carry?1:0);
OP_SBB: Y = A - B - (carry?1:0);
OP_ROL: Y = {A, carry};
OP_ROR: Y = {A[0], carry, A[7:1]};
// unary operations
`OP_LOAD_A: Y = {1'b0, A};
`OP_LOAD_B: Y = {1'b0, B};
`OP_INC: Y = A + 1;
`OP_DEC: Y = A - 1;
// unary operations that generate and/or use carry
`OP_ASL: Y = {A, 1'b0};
`OP_LSR: Y = {A[0], 1'b0, A[N-1:1]};
`OP_ROL: Y = {A, carry};
`OP_ROR: Y = {A[0], carry, A[N-1:1]};
// binary operations
`OP_OR: Y = {1'b0, A | B};
`OP_AND: Y = {1'b0, A & B};
`OP_XOR: Y = {1'b0, A ^ B};
`OP_ZERO: Y = 0;
// binary operations that generate and/or use carry
`OP_ADD: Y = A + B;
`OP_SUB: Y = A - B;
`OP_ADC: Y = A + B + (carry?1:0);
`OP_SBB: Y = A - B - (carry?1:0);
endcase
endmodule
@ -68,31 +72,32 @@ tttt = flags test for conditional branch
*/
// destinations for COMPUTE instructions
parameter DEST_A = 2'b00;
parameter DEST_B = 2'b01;
parameter DEST_IP = 2'b10;
parameter DEST_NOP = 2'b11;
`define DEST_A 2'b00
`define DEST_B 2'b01
`define DEST_IP 2'b10
`define DEST_NOP 2'b11
// instruction macros
`define I_COMPUTE(dest,op) { 2'b00, 2'(dest), 4'(op) }
`define I_COMPUTE_IMM(dest,op) { 2'b01, 2'(dest), 4'(op) }
`define I_COMPUTE_READB(dest,op) { 2'b11, 2'(dest), 4'(op) }
`define I_CONST_IMM_A { 2'b01, DEST_A, OP_LOAD_B }
`define I_CONST_IMM_B { 2'b01, DEST_B, OP_LOAD_B }
`define I_JUMP_IMM { 2'b01, DEST_IP, OP_LOAD_B }
`define I_STORE_A(addr) { 4'b1001, 4'(addr) }
`define I_BRANCH_IF(zv,zu,cv,cu) { 4'b1010, 1'(zv), 1'(zu), 1'(cv), 1'(cu) }
`define I_COMPUTE(dest,op) { 2'b00, (dest), (op) }
`define I_COMPUTE_IMM(dest,op) { 2'b01, (dest), (op) }
`define I_COMPUTE_READB(dest,op) { 2'b11, (dest), (op) }
`define I_CONST_IMM_A { 2'b01, `DEST_A, `OP_LOAD_B }
`define I_CONST_IMM_B { 2'b01, `DEST_B, `OP_LOAD_B }
`define I_JUMP_IMM { 2'b01, `DEST_IP, `OP_LOAD_B }
`define I_STORE_A(addr) { 4'b1001, (addr) }
`define I_BRANCH_IF(zv,zu,cv,cu) { 4'b1010, (zv), (zu), (cv), (cu) }
`define I_CLEAR_CARRY { 8'b10001000 }
`define I_SWAP_AB { 8'b10000001 }
`define I_RESET { 8'b10111111 }
// convenience macros
`define I_ZERO_A `I_COMPUTE(DEST_A, OP_ZERO)
`define I_ZERO_B `I_COMPUTE(DEST_B, OP_ZERO)
`define I_BRANCH_IF_CARRY(carry) `I_BRANCH_IF(0,0,carry,1)
`define I_BRANCH_IF_ZERO(zero) `I_BRANCH_IF(zero,1,0,0)
`define I_CLEAR_ZERO `I_COMPUTE(DEST_NOP,OP_ZERO)
`define I_ZERO_A `I_COMPUTE(`DEST_A, `OP_ZERO)
`define I_ZERO_B `I_COMPUTE(`DEST_B, `OP_ZERO)
`define I_BRANCH_IF_CARRY(carry) `I_BRANCH_IF(1'b0, 1'b0, carry, 1'b1)
`define I_BRANCH_IF_ZERO(zero) `I_BRANCH_IF(zero, 1'b1, 1'b0, 1'b0)
`define I_CLEAR_ZERO `I_COMPUTE(`DEST_NOP, `OP_ZERO)
module CPU(clk, reset, address, data_in, data_out, write);
input clk;
input reset;
output [7:0] address;
@ -206,13 +211,13 @@ module CPU(clk, reset, address, data_in, data_out, write);
S_COMPUTE: begin
// transfer ALU output to destination
case (opdest)
DEST_A: A <= Y[7:0];
DEST_B: B <= Y[7:0];
DEST_IP: IP <= Y[7:0];
DEST_NOP: ;
`DEST_A: A <= Y[7:0];
`DEST_B: B <= Y[7:0];
`DEST_IP: IP <= Y[7:0];
`DEST_NOP: ;
endcase
// set carry for certain operations (code >= 8)
if (aluop[3]) carry <= Y[8];
// set carry for certain operations (4-7,12-15)
if (aluop[2]) carry <= Y[8];
// set zero flag
zero <= ~|Y[7:0];
// repeat CPU loop
@ -275,15 +280,24 @@ module test_CPU_top(
`I_ZERO_A,
`I_CONST_IMM_B,
1,
`I_COMPUTE(DEST_A, OP_ADC), // addr 4
`I_COMPUTE(`DEST_A, `OP_ADC), // addr 4
`I_SWAP_AB,
`I_BRANCH_IF_CARRY(0),
`I_BRANCH_IF_CARRY(1'b0),
4 + 'h80, // correct for ROM offset
`I_STORE_A(0),
`I_STORE_A(4'd0),
`I_RESET,
// leftover elements
118{0}
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0
};
end
endmodule
`endif

View File

@ -82,10 +82,12 @@ module digits10_array(digit, yofs, bits);
input [2:0] yofs;
output [4:0] bits;
reg [4:0] bitarray[16][5];
reg [4:0] bitarray[0:15][0:4];
assign bits = bitarray[digit][yofs];
integer i,j;
initial begin/*{w:5,h:5,count:10}*/
bitarray[0][0] = 5'b11111;
bitarray[0][1] = 5'b10001;
@ -148,8 +150,8 @@ module digits10_array(digit, yofs, bits);
bitarray[9][4] = 5'b11111;
// clear unused array entries
for (int i = 10; i <= 15; i++)
for (int j = 0; j <= 4; j++)
for (i = 10; i <= 15; i++)
for (j = 0; j <= 4; j++)
bitarray[i][j] = 0;
end
endmodule

View File

@ -0,0 +1,90 @@
; Architecture file for the FEMTO-8
; default output format is a memory initialization file
.outfmt mif
; mif file is this big
.mifwords 256
.mifwidth 16
; Opcodes for core instruction set
.define r0 0
.define r1 1
.define r2 2
.define r3 3
.define r4 4
.define r5 5
.define r6 6
.define r7 7
.define ip 7
.define LOAD_A 0
.define LOAD_B 1
.define INC 2
.define DEC 3
.define ASL 4
.define LSR 5
.define ROL 6
.define ROR 7
.define OR 8
.define AND 9
.define XOR 10
.define ZERO 11
.define ADD 12
.define SUB 13
.define ADC 14
.define SBB 15
; reg-reg op
reg 4 3 3 { 00000 (1) 0 (0) (2) }
mem 4 3 3 { 00001 (1) 0 (0) (2) }
imm 3 3 8 { 11 (0) (1) (2) }
mov 3 3 { 00000 (0) 00001 (1) }
or 3 3 { 00000 (0) 01000 (1) }
and 3 3 { 00000 (0) 01001 (1) }
xor 3 3 { 00000 (0) 01010 (1) }
add 3 3 { 00000 (0) 01100 (1) }
sub 3 3 { 00000 (0) 01101 (1) }
adc 3 3 { 00000 (0) 01110 (1) }
sbb 3 3 { 00000 (0) 01111 (1) }
inc 3 { 00000 (0) 00010 (0) }
dec 3 { 00000 (0) 00011 (0) }
asl 3 { 00000 (0) 00100 (0) }
lsr 3 { 00000 (0) 00101 (0) }
rol 3 { 00000 (0) 00110 (0) }
ror 3 { 00000 (0) 00111 (0) }
zero 3 { 00000 (0) 01011 (0) }
load 3 3 { 00001 (0) 00001 (1) }
lor 3 3 { 00001 (0) 01000 (1) }
land 3 3 { 00001 (0) 01001 (1) }
lxor 3 3 { 00001 (0) 01010 (1) }
ladd 3 3 { 00001 (0) 01100 (1) }
lsub 3 3 { 00001 (0) 01101 (1) }
ladc 3 3 { 00001 (0) 01110 (1) }
lsbb 3 3 { 00001 (0) 01111 (1) }
ior 3 8 { 11 000 (0) (1) }
iand 3 8 { 11 001 (0) (1) }
ixor 3 8 { 11 010 (0) (1) }
iadd 3 8 { 11 100 (0) (1) }
isub 3 8 { 11 101 (0) (1) }
iadc 3 8 { 11 110 (0) (1) }
isbb 3 8 { 11 111 (0) (1) }
constb 3 8 { 00100 (0) (1) }
loadz 3 8 { 00101 (0) (1) }
storez 3 8 { 00110 (0) (1) }
branch 3 8 { 10 (0) 111 (1) }
bcc 8 { 10 001 111 (0) }
bcs 8 { 10 101 111 (0) }
bnz 8 { 10 010 111 (0) }
bz 8 { 10 110 111 (0) }
reset { 1011100011111111 }
; allow raw byte positioning
byte 8 { (0) } ; One byte constant

View File

@ -0,0 +1,99 @@
; Architecture file for the FEMTO-8
; default output format is a memory initialization file
.outfmt mif
; mif file is this big
.mifwords 256
.mifwidth 8
; Opcodes for core instruction set
.define A 0
.define B 1
.define IP 2
.define NOP 3
.define LOAD_A 0000
.define LOAD_B 0001
.define INC 0010
.define DEC 0011
.define ASL 0100
.define LSR 0101
.define ROL 0110
.define ROR 0111
.define OR 1000
.define AND 1001
.define XOR 1010
.define ZERO 1011
.define ADD 1100
.define SUB 1101
.define ADC 1110
.define SBB 1111
; ALU A + B -> dest
mova 2 { 00 (0) 0000 }
movb 2 { 00 (0) 0001 }
inc 2 { 00 (0) 0010 }
dec 2 { 00 (0) 0011 }
asl 2 { 00 (0) 0100 }
lsr 2 { 00 (0) 0101 }
rol 2 { 00 (0) 0110 }
ror 2 { 00 (0) 0111 }
or 2 { 00 (0) 1000 }
and 2 { 00 (0) 1001 }
xor 2 { 00 (0) 1010 }
zero 2 { 00 (0) 1011 }
add 2 { 00 (0) 1100 }
sub 2 { 00 (0) 1101 }
adc 2 { 00 (0) 1110 }
sbb 2 { 00 (0) 1111 }
; ALU A + immediate -> dest
movi 2 8 { 01 (0) 0001 (1) }
asli 2 8 { 01 (0) 0100 (1) }
lsri 2 8 { 01 (0) 0101 (1) }
roli 2 8 { 01 (0) 0110 (1) }
rori 2 8 { 01 (0) 0111 (1) }
ori 2 8 { 01 (0) 1000 (1) }
andi 2 8 { 01 (0) 1001 (1) }
xori 2 8 { 01 (0) 1010 (1) }
addi 2 8 { 01 (0) 1100 (1) }
subi 2 8 { 01 (0) 1101 (1) }
adci 2 8 { 01 (0) 1110 (1) }
sbbi 2 8 { 01 (0) 1111 (1) }
; ALU A + read [B] -> dest
movrb 2 { 11 (0) 0001 }
aslrb 2 { 11 (0) 0100 }
lsrrb 2 { 11 (0) 0101 }
rolrb 2 { 11 (0) 0110 }
rorrb 2 { 11 (0) 0111 }
orrb 2 { 11 (0) 1000 }
andrb 2 { 11 (0) 1001 }
xorrb 2 { 11 (0) 1010 }
addrb 2 { 11 (0) 1100 }
subrb 2 { 11 (0) 1101 }
adcrb 2 { 11 (0) 1110 }
sbbrb 2 { 11 (0) 1111 }
; A -> write [nnnn]
sta 4 { 1001 (0) }
; other insns
clc { 10001000 }
swapab { 10000001 }
reset { 10001111 }
lia 8 { 01 00 0001 (0) }
lib 8 { 01 01 0001 (0) }
jmp 8 { 01 10 0001 (0) }
; conditional branch
bcc 8 { 1010 0001 (0) }
bcs 8 { 1010 0011 (0) }
bz 8 { 1010 1100 (0) }
bnz 8 { 1010 0100 (0) }
; allow raw byte positioning
byte 8 { (0) } ; One byte constant

View File

@ -0,0 +1,182 @@
`include "hvsync_generator.v"
`include "sprite_bitmap.v"
`include "sprite_renderer.v"
`include "cpu8.v"
// uncomment to see scope view
//`define DEBUG
module frame_buffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
address_bus, to_cpu, from_cpu, write_enable
`ifdef DEBUG
, output [7:0] A
, output [7:0] B
, output [7:0] IP
, output carry
, output zero
`else
,rgb
`endif
);
input clk, reset;
input hpaddle, vpaddle;
output hsync, vsync;
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
`ifdef DEBUG
assign IP = cpu.IP;
assign A = cpu.A;
assign B = cpu.B;
assign carry = cpu.carry;
assign zero = cpu.zero;
`else
output [3:0] rgb;
`endif
parameter IN_HPOS = 8'b01000000;
parameter IN_VPOS = 8'b01000001;
parameter IN_FLAGS = 8'b01000010;
parameter IN_VPU = 8'b01000011;
reg [7:0] ram[0:63];
reg [7:0] rom[0:255];
output wire [7:0] address_bus;
output reg [7:0] to_cpu;
output wire [7:0] from_cpu;
output wire write_enable;
CPU cpu(.clk(clk),
.reset(reset),
.address(address_bus),
.data_in(to_cpu),
.data_out(from_cpu),
.write(write_enable));
always @(posedge clk)
if (write_enable) begin
casez (address_bus)
// VPU lo byte
8'b0001000: begin
vpu_write[15:8] <= from_cpu;
end
// VPU hi byte
8'b0001001: begin
vpu_write[7:0] <= from_cpu;
end
// VPU write
8'b0001010: begin
vpu_ram[vpu_write] <= from_cpu[7:4];
vpu_ram[vpu_write+1] <= from_cpu[3:0];
vpu_write <= vpu_write + 2;
end
// VPU move
8'b0001011: begin
// sign extend
vpu_write <= {
vpu_write[15:8] + { {4{from_cpu[7]}}, from_cpu[7:4] },
vpu_write[7:0] + { {4{from_cpu[3]}}, from_cpu[3:0] }
};
end
default: ram[address_bus[5:0]] <= from_cpu;
endcase
end
always @(*)
casez (address_bus)
// RAM
8'b00??????: to_cpu = ram[address_bus[5:0]];
// special read registers
IN_HPOS: to_cpu = hpos[7:0];
IN_VPOS: to_cpu = vpos[7:0];
IN_FLAGS: to_cpu = {3'b0,
vsync, hsync, vpaddle, hpaddle, display_on};
IN_VPU: to_cpu = {vpu_ram[vpu_write], vpu_ram[vpu_write+1]};
// ROM
8'b1???????: to_cpu = rom[address_bus[6:0] + 128];
default: ;
endcase
hvsync_generator hvsync_gen(
.clk(clk),
.reset(0),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
.hpos(hpos),
.vpos(vpos)
);
reg [3:0] vpu_ram[0:65535];
reg [15:0] vpu_read;
reg [15:0] vpu_write;
reg [3:0] rgb;
always @(posedge clk) begin
if (!hpos[8] && !vpos[8]) begin
rgb <= vpu_ram[vpu_read];
vpu_read <= vpu_read + 1;
end else begin
rgb <= 0;
if (vpos[8])
vpu_read <= 0;
end
end
`ifdef EXT_INLINE_ASM
initial begin
rom = '{
__asm
.arch femto8
.org 128
.define VPU_LO 8
.define VPU_HI 9
.define VPU_WRITE 10
.define VPU_MOVE 11
.define IN_HPOS $40
.define IN_VPOS $41
.define IN_FLAGS $42
.define F_DISPLAY 1
.define F_HPADDLE 2
.define F_VPADDLE 4
.define F_HSYNC 8
.define F_VSYNC 16
Start:
zero A
sta VPU_LO
sta VPU_HI
sta 0
sta 1
DisplayLoop:
zero B
movrb A
sta VPU_WRITE
sta VPU_MOVE
sta VPU_WRITE
sta VPU_MOVE
sta VPU_WRITE
sta VPU_MOVE
lia F_VSYNC
lib IN_FLAGS
andrb NOP
bz DisplayLoop
WaitVsync:
andrb NOP
bnz WaitVsync
zero B
movrb A
inc A
sta 0
jmp DisplayLoop
__endasm
};
end
`endif
endmodule

View File

@ -13,4 +13,5 @@ module gates(clk, out_not, out_and, out_or, out_xor, in);
in <= in + 1;
end
endmodule;
endmodule

View File

@ -1,24 +1,6 @@
`ifndef HVSYNC_GENERATOR_H
`define HVSYNC_GENERATOR_H
// constant declarations for TV-simulator sync parameters
// horizontal
localparam H_DISPLAY = 256; // horizontal display width
localparam H_BACK = 23; // horizontal left border (back porch)
localparam H_FRONT = 7; // horizontal right border (front porch)
localparam H_SYNC = 23; // horizontal sync width
localparam H_SYNC_START = H_DISPLAY + H_FRONT;
localparam H_SYNC_END = H_DISPLAY + H_FRONT + H_SYNC - 1;
localparam H_MAX = H_DISPLAY + H_BACK + H_FRONT + H_SYNC - 1;
// vertical
localparam V_DISPLAY = 240; // vertical display height
localparam V_TOP = 5; // vertical top border
localparam V_BOTTOM = 14; // vertical bottom border
localparam V_SYNC = 3; // vertical sync # lines
localparam V_SYNC_START = V_DISPLAY + V_BOTTOM;
localparam V_SYNC_END = V_DISPLAY + V_BOTTOM + V_SYNC - 1;
localparam V_MAX = V_DISPLAY + V_TOP + V_BOTTOM + V_SYNC - 1;
module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);
input clk;
@ -28,6 +10,25 @@ module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);
output [8:0] hpos;
output [8:0] vpos;
// constant declarations for TV-simulator sync parameters
// horizontal
parameter H_DISPLAY = 256; // horizontal display width
parameter H_BACK = 23; // horizontal left border (back porch)
parameter H_FRONT = 7; // horizontal right border (front porch)
parameter H_SYNC = 23; // horizontal sync width
// vertical
parameter V_DISPLAY = 240; // vertical display height
parameter V_TOP = 5; // vertical top border
parameter V_BOTTOM = 14; // vertical bottom border
parameter V_SYNC = 3; // vertical sync # lines
// derived
parameter H_SYNC_START = H_DISPLAY + H_FRONT;
parameter H_SYNC_END = H_DISPLAY + H_FRONT + H_SYNC - 1;
parameter H_MAX = H_DISPLAY + H_BACK + H_FRONT + H_SYNC - 1;
parameter V_SYNC_START = V_DISPLAY + V_BOTTOM;
parameter V_SYNC_END = V_DISPLAY + V_BOTTOM + V_SYNC - 1;
parameter V_MAX = V_DISPLAY + V_TOP + V_BOTTOM + V_SYNC - 1;
wire hmaxxed = (hpos == H_MAX) || reset;
wire vmaxxed = (vpos == V_MAX) || reset;

View File

@ -1,3 +1,5 @@
`ifndef LFSR_V
`define LFSR_V
module LFSR(clk,reset,enable,lfsr);
@ -19,5 +21,6 @@ module LFSR(clk,reset,enable,lfsr);
lfsr <= {lfsr[NBITS-2:0], 1'b0} ^ (feedback ? TAPS : 0);
end
endmodule;
endmodule
`endif

View File

@ -3,32 +3,44 @@
module sound_psg(clk, reset, out, reg_sel, reg_data, reg_write);
input clk, reset;
input [3:0] reg_sel;
input [7:0] reg_data;
input reg_write;
output [7:0] out;
input [3:0] reg_sel; // select register #
input [7:0] reg_data; // data to write to register
input reg_write; // write enable
output [7:0] out; // output waveform
parameter NVOICES = 4;
reg outputs[NVOICES];
reg [17:0] count[NVOICES];
reg [7:0] register[16];
reg outputs[0:NVOICES-1];
reg [11:0] count[0:NVOICES-1];
reg [7:0] register[0:15];
reg [5:0] div64;
integer i;
always @(posedge clk) begin
out = 0;
for (i=0; i<NVOICES; i++) begin
if (count[i][17:6] == {register[i*2+1][3:0], register[i*2]}) begin
outputs[i] <= outputs[i] ^ 1;
count[i] <= 0;
end else begin
count[i] <= count[i] + 1;
end
/* verilator lint_off BLKSEQ */
if (register[15][i] && outputs[i]) begin
out = out + 1;
// divide clock by 64
if (div64 == 0) begin
out = 0; // initialize waveform output
// generate code for each voice (0..NVOICES-1)
for (i=0; i<NVOICES; i++) begin
// divide clock/64 by waveform frequency
if (count[i] == {register[i+4][3:0], register[i]}) begin
outputs[i] <= outputs[i] ^ 1;
count[i] <= 0;
end else begin
count[i] <= count[i] + 1;
end
// need this directive to mix
// blocking and non-blocking assignments
/* verilator lint_off BLKSEQ */
if (register[15][i] && outputs[i]) begin
// add to output waveform, scaled by intensity
out = out + register[i+8];
end
end
end
div64 <= div64 + 1;
// write to register?
if (reg_write) begin
register[reg_sel] <= reg_data;
end
@ -46,12 +58,14 @@ module music_player(clk, reset, advance,
output [3:0] psg_sel;
output [7:0] psg_data;
output psg_write;
integer i;
//./mknotes.py -m 12 -f 75898
// Namespace(bias=0, freq=75898.0, length=64, maxbits=12.0, upper=49)
// 434.7 3.23120101673 49
reg [11:0] note_table[64] = '{
reg [11:0] note_table[0:63] = '{
2960, 2794, 2637, 2489, 2349, 2217, 2093, 1975,
1864, 1760, 1661, 1568, 1480, 1397, 1318, 1244,
1175, 1109, 1046, 988, 932, 880, 831, 784,
@ -62,16 +76,16 @@ module music_player(clk, reset, advance,
117, 110, 104, 98, 92, 87, 82, 78
};
reg [7:0] music_table[292] = '{
reg [7:0] music_table[0:291] = '{
8'h1e,8'h12,8'h8c,8'h23,8'h17,8'h86,8'h2f,8'h86,8'h36,8'h2a,8'h27,8'h86,8'h2f,8'h86,8'h33,8'h1e,8'h23,8'h86,8'h36,8'h2a,8'h86,8'h24,8'h18,8'h86,8'h2e,8'h86,8'h2a,8'h36,8'h25,8'h86,8'h2e,8'h86,8'h31,8'h28,8'h22,8'h86,8'h36,8'h2a,8'h86,8'h1e,8'h22,8'h28,8'h8c,8'h1e,8'h12,8'h8c,8'h23,8'h17,8'h86,8'h2f,8'h86,8'h36,8'h2a,8'h27,8'h86,8'h2f,8'h86,8'h33,8'h1e,8'h23,8'h86,8'h36,8'h2a,8'h86,8'h24,8'h18,8'h86,8'h2e,8'h86,8'h36,8'h2a,8'h25,8'h86,8'h2e,8'h86,8'h31,8'h28,8'h22,8'h86,8'h36,8'h2a,8'h86,8'h28,8'h22,8'h1e,8'h8c,8'h12,8'h1e,8'h86,8'h36,8'h2a,8'h86,8'h1f,8'h13,8'h86,8'h2f,8'h86,8'h32,8'h86,8'h37,8'h2b,8'h86,8'h1e,8'h12,8'h86,8'h36,8'h2a,8'h86,8'h1e,8'h12,8'h86,8'h2a,8'h36,8'h86,8'h1f,8'h13,8'h86,8'h2f,8'h86,8'h32,8'h86,8'h37,8'h2b,8'h86,8'h1e,8'h12,8'h86,8'h36,8'h2a,8'h92,8'h0b,8'h86,8'h17,8'h86,8'h1a,8'h86,8'h23,8'h86,8'h17,8'h86,8'h23,8'h86,8'h26,8'h86,8'h2f,8'h86,8'h23,8'h86,8'h2f,8'h86,8'h32,8'h86,8'h3b,8'h86,8'h2f,8'h86,8'h3b,8'h86,8'h3e,8'h86,8'h86,8'h3b,8'h29,8'h2c,8'h8c,8'h3b,8'h32,8'h2f,8'h8c,8'h3b,8'h32,8'h2f,8'h8c,8'h3b,8'h29,8'h2c,8'h86,8'h3b,8'h86,8'h33,8'h2f,8'h2a,8'h86,8'h86,8'h33,8'h2f,8'h2a,8'h86,8'h3f,8'h86,8'h2a,8'h2f,8'h33,8'h86,8'h3b,8'h86,8'h33,8'h2f,8'h2a,8'h86,8'h37,8'h3b,8'h86,8'h32,8'h2f,8'h2b,8'h86,8'h3d,8'h86,8'h3e,8'h37,8'h2b,8'h86,8'h3b,8'h86,8'h3d,8'h33,8'h2f,8'h86,8'h3f,8'h36,8'h86,8'h33,8'h2f,8'h2a,8'h86,8'h3b,8'h86,8'h3f,8'h36,8'h33,8'h86,8'h3b,8'h86,8'h3d,8'h36,8'h2a,8'h8c,8'h3b,8'h36,8'h33,8'h92,8'h3b,8'h2f,8'h86,8'h26,8'h23,8'h20,8'h8c,8'h3b,8'h2f,8'h1d,8'h8c,8'h3b,8'h2f,8'h26,8'h8c,8'h3b,8'h2f,8'h26,8'h86,8'h3b,8'h2f,8'h86,8'h1e,8'h23,8'h27,8'h86,8'h36,8'h86,8'h38,8'h2f,8'h27,8'h86,8'h33,8'h86,8'h36,8'h27,8'h23,8'h86,8'h38,8'h2f,8'h86,8'h1e,8'h23,8'h27,8'h86,8'h2f,8'h2b,8'h86,8'h26,8'hff
};
reg [15:0] music_ptr = 0;
reg [2:0] freech = 0;
reg [7:0] cur_duration = 0;
reg [3:0] ch_durations[NVOICES];
reg [3:0] ch_durations[0:NVOICES-1];
reg [7:0] psg_regs[16];
reg [7:0] psg_regs[0:15];
reg [3:0] next_reg;
wire [7:0] note = music_table[music_ptr[8:0]];
@ -88,18 +102,19 @@ module music_player(clk, reset, advance,
if (note[7]) begin
cur_duration <= note & 63;
end else begin
psg_regs[freech*2] <= period[7:0];
psg_regs[freech*2+1] <= 8'(period[11:8]);
ch_durations[freech+0] = 4;
psg_regs[freech+0] <= period[7:0];
psg_regs[freech+4] <= {4'b0, period[11:8]};
ch_durations[freech+0] = 7;
psg_regs[15][freech] <= 1;
freech <= (freech == NVOICES-1) ? 0 : freech+1;
end
end else begin
cur_duration <= cur_duration - 1;
for (int i=0; i<NVOICES; i++) begin
for (i=0; i<NVOICES; i++) begin
if (ch_durations[i] == 0) begin
psg_regs[15][i] <= 0;
end else begin
psg_regs[freech+8] <= {4'b0, ch_durations[i]};
ch_durations[i] <= ch_durations[i] - 1;
end
end
@ -113,7 +128,7 @@ module music_player(clk, reset, advance,
next_reg <= next_reg + 1;
end
endmodule;
endmodule
module top(clk, reset, hsync, vsync, rgb, spkr);

View File

@ -1,84 +0,0 @@
; Architecture file for the NANO8
; default output format is a memory initialization file
.outfmt mif
; mif file is this big
.mifwords 256
.mifwidth 8
; Opcodes for core instruction set
.define A 0
.define B 1
.define IP 2
.define NOP 3
.define ADD 8
; ALU A + B -> dest
mova 2 { 00 (0) 0000 }
movb 2 { 00 (0) 0001 }
inc 2 { 00 (0) 0010 }
dec 2 { 00 (0) 0011 }
or 2 { 00 (0) 0100 }
and 2 { 00 (0) 0101 }
xor 2 { 00 (0) 0110 }
zero 2 { 00 (0) 0111 }
add 2 { 00 (0) 1000 }
sub 2 { 00 (0) 1001 }
asl 2 { 00 (0) 1010 }
lsr 2 { 00 (0) 1011 }
adc 2 { 00 (0) 1100 }
sbb 2 { 00 (0) 1101 }
rol 2 { 00 (0) 1110 }
ror 2 { 00 (0) 1111 }
; ALU A + immediate -> dest
movi 2 8 { 01 (0) 0001 (1) }
ori 2 8 { 01 (0) 0100 (1) }
andi 2 8 { 01 (0) 0101 (1) }
xori 2 8 { 01 (0) 0110 (1) }
addi 2 8 { 01 (0) 1000 (1) }
subi 2 8 { 01 (0) 1001 (1) }
asli 2 8 { 01 (0) 1010 (1) }
lsri 2 8 { 01 (0) 1011 (1) }
adci 2 8 { 01 (0) 1100 (1) }
sbbi 2 8 { 01 (0) 1101 (1) }
roli 2 8 { 01 (0) 1110 (1) }
rori 2 8 { 01 (0) 1111 (1) }
; ALU A + read [B] -> dest
movrb 2 { 11 (0) 0001 }
orrb 2 { 11 (0) 0100 }
andrb 2 { 11 (0) 0101 }
xorrb 2 { 11 (0) 0110 }
addrb 2 { 11 (0) 1000 }
subrb 2 { 11 (0) 1001 }
aslrb 2 { 11 (0) 1010 }
lsrrb 2 { 11 (0) 1011 }
adcrb 2 { 11 (0) 1100 }
sbbrb 2 { 11 (0) 1101 }
rolrb 2 { 11 (0) 1110 }
rorrb 2 { 11 (0) 1111 }
; A -> write [nnnn]
sta 4 { 1001 (0) }
; other insns
clc { 10001000 }
swapab { 10000001 }
reset { 10001111 }
lda 8 { 01 00 0001 (0) }
ldb 8 { 01 01 0001 (0) }
jmp 8 { 01 10 0001 (0) }
; conditional branch
bcc 8 { 1010 0001 (0) }
bcs 8 { 1010 0011 (0) }
bz 8 { 1010 1100 (0) }
bnz 8 { 1010 0100 (0) }
; allow raw byte positioning
byte 8 { (0) } ; One byte constant

View File

@ -1,11 +1,12 @@
`include "hvsync_generator.v"
`include "sprite_bitmap.v"
`include "sprite_renderer.v"
`include "cpu8.v"
// uncomment to see scope view
//`define DEBUG
module sprite_multiple_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
address_bus, to_cpu, from_cpu, write_enable
`ifdef DEBUG
, output [7:0] A
@ -14,7 +15,7 @@ module sprite_multiple_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
, output carry
, output zero
`else
, output [2:0] rgb
, rgb
`endif
);
@ -31,6 +32,8 @@ module sprite_multiple_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
assign B = cpu.B;
assign carry = cpu.carry;
assign zero = cpu.zero;
`else
output [3:0] rgb;
`endif
parameter PADDLE_X = 0;
@ -128,10 +131,28 @@ module sprite_multiple_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
.gfx(enemy_gfx),
.in_progress(player_is_drawing));
reg frame_collision;
always @(posedge clk)
if (player_gfx && (enemy_gfx || track_gfx))
frame_collision <= 1;
else if (vpos==0)
frame_collision <= 0;
wire track_offside = (hpos[7:5]==0) || (hpos[7:5]==7);
wire track_shoulder = (hpos[7:3]==3) || (hpos[7:3]==28);
wire track_gfx = (vpos[5:1]!=ram[TRACKPOS_LO][5:1]) && track_offside;
wire r = display_on && (player_gfx || enemy_gfx || track_shoulder);
wire g = display_on && (player_gfx || track_gfx);
wire b = display_on && (enemy_gfx || track_shoulder);
assign rgb = {1'b0,b,g,r};
`ifdef EXT_INLINE_ASM
initial begin
rom = '{
__asm
.arch nano8
.arch femto8
.org 128
.define PADDLE_X 0
@ -157,11 +178,11 @@ module sprite_multiple_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
.define F_COLLIDE 32
Start:
lda 128
lia 128
sta PLAYER_X
sta ENEMY_X
sta ENEMY_Y
lda 180
lia 180
sta PLAYER_Y
zero A
sta SPEED
@ -169,17 +190,17 @@ Start:
sta ENEMY_DIR
; test hpaddle flag
DisplayLoop:
lda F_HPADDLE
ldb IN_FLAGS
lia F_HPADDLE
lib IN_FLAGS
andrb NOP
bz DisplayLoop
; [vpos] -> paddle_x
ldb IN_VPOS
lib IN_VPOS
movrb A
sta PLAYER_X
; wait for vsync=1 then vsync=0
lda F_VSYNC
ldb IN_FLAGS
lia F_VSYNC
lib IN_FLAGS
WaitForVsyncOn:
andrb NOP
bz WaitForVsyncOn
@ -187,16 +208,16 @@ WaitForVsyncOff:
andrb NOP
bnz WaitForVsyncOff
; check collision
lda F_COLLIDE
ldb IN_FLAGS
lia F_COLLIDE
lib IN_FLAGS
andrb NOP
bz NoCollision
; load slow speed
lda 16
lia 16
sta SPEED
NoCollision:
; update speed
ldb SPEED
lib SPEED
movrb A
inc A
; don't store if == 0
@ -209,19 +230,19 @@ MaxSpeed:
lsr A
lsr A
; add to lo byte of track pos
ldb TRACKPOS_LO
lib TRACKPOS_LO
addrb B
swapab
sta TRACKPOS_LO
swapab
; update enemy vert pos
ldb ENEMY_Y
lib ENEMY_Y
addrb A
sta ENEMY_Y
; update enemy horiz pos
ldb ENEMY_X
lib ENEMY_X
movrb A
ldb ENEMY_DIR
lib ENEMY_DIR
addrb A
sta ENEMY_X
subi A 64
@ -237,22 +258,6 @@ SkipXReverse:
__endasm
};
end
`endif
reg frame_collision;
always @(posedge clk)
if (player_gfx && (enemy_gfx || track_gfx))
frame_collision <= 1;
else if (vpos==0)
frame_collision <= 0;
wire track_offside = (hpos[7:5]==0) || (hpos[7:5]==7);
wire track_shoulder = (hpos[7:3]==3) || (hpos[7:3]==28);
wire track_gfx = (vpos[5:1]!=ram[TRACKPOS_LO][5:1]) && track_offside;
wire r = display_on && (player_gfx || enemy_gfx || track_shoulder);
wire g = display_on && (player_gfx || track_gfx);
wire b = display_on && (enemy_gfx || track_shoulder);
assign rgb = {b,g,r};
endmodule

View File

@ -9,7 +9,7 @@ module RAM_1KB(clk, addr, din, dout, we);
output [7:0] dout; // 8-bit data output
input we; // write enable
reg [7:0] mem [1024]; // 1024x8 bit memory
reg [7:0] mem [0:1023]; // 1024x8 bit memory
always @(posedge clk) begin
if (we) // if write enabled
@ -19,7 +19,7 @@ module RAM_1KB(clk, addr, din, dout, we);
endmodule
module test_framebuf_top(clk, reset, hsync, vsync, rgb);
module test_ram1_top(clk, reset, hsync, vsync, rgb);
input clk, reset;
output hsync, vsync;

View File

@ -15,7 +15,7 @@ module RAM_1KB_tri(clk, addr, data, we);
end
endmodule
module test_framebuf_top(
module test_ram2_top(
input clk, reset,
output hsync, vsync,
output [2:0] rgb

View File

@ -1,3 +1,6 @@
`ifndef SCOREBOARD_H
`define SCOREBOARD_H
`include "hvsync_generator.v"
`include "digits10.v"
@ -65,7 +68,7 @@ module scoreboard_generator(score0, score1, lives, vpos, hpos, board_gfx);
endmodule
module top(clk, reset, hsync, vsync, rgb);
module scoreboard_top(clk, reset, hsync, vsync, rgb);
input clk, reset;
output hsync, vsync;
@ -101,3 +104,5 @@ module top(clk, reset, hsync, vsync, rgb);
assign rgb = {b,g,r};
endmodule
`endif

View File

@ -1,251 +0,0 @@
`define OP_LOAD_A 4'h0
`define OP_LOAD_B 4'h1
`define OP_OR 4'h2
`define OP_AND 4'h3
`define OP_XOR 4'h4
`define OP_INC 4'h5
`define OP_DEC 4'h6
`define OP_NOP 4'h7
// operations that generate carry
`define OP_ADD 4'h8
`define OP_SUB 4'h9
`define OP_ASL 4'ha
`define OP_LSR 4'hb
module ALU(
input [7:0] A,
input [7:0] B,
output [8:0] Y,
input [3:0] aluop
);
always @(*)
case (aluop)
`OP_LOAD_A: Y = {1'b0, A};
`OP_LOAD_B: Y = {1'b0, B};
`OP_OR: Y = {1'b0, A | B};
`OP_AND: Y = {1'b0, A & B};
`OP_XOR: Y = {1'b0, A ^ B};
`OP_INC: Y = A + 1;
`OP_DEC: Y = A - 1;
`OP_ADD: Y = A + B;
`OP_SUB: Y = A - B;
`OP_ASL: Y = A + A;
`OP_LSR: Y = {A[0], A >> 1};
default: Y = 9'bx;
endcase
endmodule
/*
Bits Description
00ddaaaa A + B -> dest
01ddaaaa A + immediate -> dest
11ddaaaa A + read [B] -> dest
10000001 swap A <-> B
1001nnnn A -> write [nnnn]
1010tttt conditional branch
dd = destination (00=A, 01=B, 10=IP, 11=none)
aaaa = ALU operation (replaces + operator)
nnnn = 4-bit constant
tttt = flags test for conditional branch
*/
`define DEST_A 2'b00
`define DEST_B 2'b01
`define DEST_IP 2'b10
`define DEST_NOP 2'b11
`define I_COMPUTE(dest,op) { 2'b00, 2'(dest), 4'(op) }
`define I_COMPUTE_IMM(dest,op) { 2'b01, 2'(dest), 4'(op) }
`define I_COMPUTE_READB(dest,op) { 2'b11, 2'(dest), 4'(op) }
`define I_CONST_IMM_A { 2'b01, `DEST_A, `OP_LOAD_B }
`define I_CONST_IMM_B { 2'b01, `DEST_B, `OP_LOAD_B }
`define I_JUMP_IMM { 2'b01, `DEST_IP, `OP_LOAD_B }
`define I_STORE_A(addr) { 4'b1001, 4'(addr) }
`define I_BRANCH_IF_CARRY(carry) { 4'b1010, 2'b00, 1'(carry), 1'b1 }
`define I_SWAP_AB { 8'b10000001 }
`define I_RESET { 8'b10111111 }
module CPU(
input clk,
input reset,
output [7:0] address,
input [7:0] data_in,
output [7:0] data_out,
output write
);
reg [7:0] IP;
reg [7:0] A, B;
reg [8:0] Y;
reg [2:0] state;
reg carry;
reg zero;
wire [1:0] flags = { zero, carry };
reg [7:0] opcode;
wire [3:0] aluop = opcode[3:0];
wire [1:0] opdest = opcode[5:4];
wire memalu = opcode[6];
localparam S_RESET = 0;
localparam S_SELECT = 1;
localparam S_DECODE = 2;
localparam S_COMPUTE = 3;
localparam S_READ_IP = 4;
ALU alu(.A(A), .B(memalu?data_in:B), .Y(Y), .aluop(aluop));
always @(posedge clk)
if (reset) begin
state <= 0;
write <= 0;
end else begin
case (state)
// state 0: reset
S_RESET: begin
IP <= 8'h80;
write <= 0;
state <= S_SELECT;
end
// state 1: select opcode address
S_SELECT: begin
address <= IP;
IP <= IP + 1;
write <= 0;
state <= S_DECODE;
end
// state 2: read/decode opcode
S_DECODE: begin
opcode <= data_in;
casez (data_in)
// ALU A + B -> dest
8'b00??????: begin
state <= S_COMPUTE;
end
// ALU A + immediate -> dest
8'b01??????: begin
address <= IP;
IP <= IP + 1;
state <= S_COMPUTE;
end
// ALU A + read [B] -> dest
8'b11??????: begin
address <= B;
state <= S_COMPUTE;
end
// A -> write [aluop]
8'b1001????: begin
address <= {4'b0, aluop};
data_out <= A;
write <= 1;
state <= S_SELECT;
end
// swap A,B
8'b10000001: begin
A <= B;
B <= A;
state <= S_SELECT;
end
// conditional branch
8'b1010????: begin
if (
(data_in[0] && (data_in[1] == carry)) ||
(data_in[2] && (data_in[3] == zero)))
begin
address <= IP;
state <= S_READ_IP;
end else begin
state <= S_SELECT;
end
IP <= IP + 1; // skip immediate
end
// fall-through RESET
default: begin
state <= S_RESET; // reset
end
endcase
end
// state 3: compute ALU op and flags
S_COMPUTE: begin
// transfer ALU output to destination
case (opdest)
`DEST_A: A <= Y[7:0];
`DEST_B: B <= Y[7:0];
`DEST_IP: IP <= Y[7:0];
`DEST_NOP: ;
endcase
// set carry for certain operations (code >= 8)
if (aluop[3]) carry <= Y[8];
// set zero flag
zero <= ~|Y;
// repeat CPU loop
state <= S_SELECT;
end
// state 4: read new IP from memory (immediate mode)
S_READ_IP: begin
IP <= data_in;
state <= S_SELECT;
end
endcase
end
endmodule
module test_CPU_top(
input clk,
input reset,
output [7:0] address_bus,
output reg [7:0] to_cpu,
output [7:0] from_cpu,
output write_enable,
output [7:0] IP,
output [7:0] A,
output [7:0] B
);
reg [7:0] ram[0:127];
reg [7:0] rom[0:127];
assign IP = cpu.IP;
assign A = cpu.A;
assign B = cpu.B;
CPU cpu(.clk(clk),
.reset(reset),
.address(address_bus),
.data_in(to_cpu),
.data_out(from_cpu),
.write(write_enable));
// does not work as (posedge clk)
always @(*)
if (write_enable)
ram[address_bus[6:0]] = from_cpu;
else if (address_bus[7] == 0)
to_cpu = ram[address_bus[6:0]];
else
to_cpu = rom[address_bus[6:0]];
initial begin
rom = '{
`I_CONST_IMM_A,
0,
`I_CONST_IMM_B,
1,
`I_COMPUTE(`DEST_A, `OP_ADD), // addr 4
`I_SWAP_AB,
`I_BRANCH_IF_CARRY(0),
4 + 'h80, // correct for ROM offset
`I_RESET,
// leftover elements
119{0}
};
end
endmodule

View File

@ -1,3 +1,6 @@
`ifndef SPRITE_BITMAP_H
`define SPRITE_BITMAP_H
`include "hvsync_generator.v"
module car_bitmap(yofs, bits);
@ -5,25 +8,27 @@ module car_bitmap(yofs, bits);
input [3:0] yofs;
output [7:0] bits;
reg [7:0] bitarray[16];
reg [7:0] bitarray[0:15];
assign bits = bitarray[yofs];
initial begin/*{w:8,h:16}*/
bitarray[0] = 8'b0;
bitarray[1] = 8'b101110;
bitarray[2] = 8'b11101110;
bitarray[3] = 8'b11111110;
bitarray[4] = 8'b11101110;
bitarray[5] = 8'b1101110;
bitarray[6] = 8'b110000;
bitarray[7] = 8'b110000;
bitarray[1] = 8'b1100;
bitarray[2] = 8'b11001100;
bitarray[3] = 8'b11111100;
bitarray[4] = 8'b11101100;
bitarray[5] = 8'b11100000;
bitarray[6] = 8'b1100000;
bitarray[7] = 8'b1110000;
bitarray[8] = 8'b110000;
bitarray[9] = 8'b1110000;
bitarray[10] = 8'b1100000;
bitarray[11] = 8'b11100000;
bitarray[12] = 8'b11101100;
bitarray[13] = 8'b11111100;
bitarray[14] = 8'b11001100;
bitarray[15] = 8'b1100;
bitarray[9] = 8'b110000;
bitarray[10] = 8'b110000;
bitarray[11] = 8'b1101110;
bitarray[12] = 8'b11101110;
bitarray[13] = 8'b11111110;
bitarray[14] = 8'b11101110;
bitarray[15] = 8'b101110;
end
endmodule
@ -76,7 +81,7 @@ module sprite_bitmap_top(clk, reset, hsync, vsync, rgb);
wire [3:0] car_bit = car_sprite_xofs>=8 ?
15-car_sprite_xofs:
car_sprite_xofs;
wire car_gfx = car_sprite_bits[3'(car_bit)];
wire car_gfx = car_sprite_bits[car_bit[2:0]];
wire r = display_on && car_gfx;
wire g = display_on && car_gfx;
@ -84,3 +89,5 @@ module sprite_bitmap_top(clk, reset, hsync, vsync, rgb);
assign rgb = {b,g,r};
endmodule
`endif

View File

@ -1,4 +1,5 @@
`include "hvsync_generator.v"
`include "sprite_bitmap.v"
`include "sprite_renderer.v"
module sprite_multiple_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
@ -39,13 +40,13 @@ module sprite_multiple_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
.yofs(car_sprite_yofs),
.bits(car_sprite_bits));
wire player_vstart = {1'0,player_y} == vpos;
wire player_hstart = {1'0,player_x} == hpos;
wire player_vstart = {1'd0,player_y} == vpos;
wire player_hstart = {1'd0,player_x} == hpos;
wire player_gfx;
wire player_is_drawing;
wire enemy_vstart = {1'0,enemy_y} == vpos;
wire enemy_hstart = {1'0,enemy_x} == hpos;
wire enemy_vstart = {1'd0,enemy_y} == vpos;
wire enemy_hstart = {1'd0,enemy_x} == hpos;
wire enemy_gfx;
wire enemy_is_drawing;

View File

@ -1,34 +1,8 @@
`include "hvsync_generator.v"
`ifndef SPRITE_RENDERER_H
`define SPRITE_RENDERER_H
module car_bitmap(yofs, bits);
input [3:0] yofs;
output [7:0] bits;
reg [7:0] bitarray[16];
assign bits = bitarray[yofs];
initial begin/*{w:8,h:16}*/
bitarray[0] = 8'b1100;
bitarray[1] = 8'b11001100;
bitarray[2] = 8'b11111100;
bitarray[3] = 8'b11101100;
bitarray[4] = 8'b11100000;
bitarray[5] = 8'b1100000;
bitarray[6] = 8'b1110000;
bitarray[7] = 8'b110000;
bitarray[8] = 8'b110000;
bitarray[9] = 8'b110000;
bitarray[10] = 8'b1100111;
bitarray[11] = 8'b11100110;
bitarray[12] = 8'b11111111;
bitarray[13] = 8'b11100110;
bitarray[14] = 8'b1110111;
bitarray[15] = 8'b110000;
end
endmodule
`include "hvsync_generator.v"
`include "sprite_bitmap.v"
module sprite_renderer(clk, vstart, load, hstart, rom_addr, rom_bits,
gfx, in_progress);
@ -37,7 +11,9 @@ module sprite_renderer(clk, vstart, load, hstart, rom_addr, rom_bits,
output [3:0] rom_addr;
input [7:0] rom_bits;
output gfx;
output in_progress = state != WAIT_FOR_VSTART;
output in_progress;
assign in_progress = state != WAIT_FOR_VSTART;
reg [2:0] state;
reg [3:0] ycount;
@ -135,9 +111,9 @@ module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
car_bitmap car(
.yofs(car_sprite_yofs),
.bits(car_sprite_bits));
wire vstart = {1'0,player_y} == vpos;
wire hstart = {1'0,player_x} == hpos;
wire vstart = {1'd0,player_y} == vpos;
wire hstart = {1'd0,player_x} == hpos;
wire car_gfx;
wire unused;
@ -169,3 +145,5 @@ module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
assign rgb = {b,g,r};
endmodule
`endif

View File

@ -1,3 +1,6 @@
`ifndef SPRITE_ROTATION_H
`define SPRITE_ROTATION_H
`include "hvsync_generator.v"
module tank_bitmap(addr, bits);
@ -97,7 +100,7 @@ module tank_bitmap(addr, bits);
end
endmodule
module sprite_renderer(clk, vstart, load, hstart, rom_addr, rom_bits,
module sprite_renderer2(clk, vstart, load, hstart, rom_addr, rom_bits,
hmirror, vmirror,
gfx, busy);
@ -262,7 +265,7 @@ module tank_controller(clk, reset, hpos, vpos, hsync, vsync,
wire vstart = {1'b0,player_y} == vpos;
wire hstart = {1'b0,player_x} == hpos;
sprite_renderer renderer(
sprite_renderer2 renderer(
.clk(clk),
.vstart(vstart),
.load(hsync),
@ -388,3 +391,5 @@ module control_test_top(clk, reset, hsync, vsync, rgb, switches_p1);
assign rgb = {b,g,r};
endmodule
`endif

View File

@ -1,7 +1,7 @@
`include "hvsync_generator.v"
`include "lfsr.v"
module top(clk, reset, hsync, vsync, rgb);
module starfield_top(clk, reset, hsync, vsync, rgb);
input clk, reset;
output hsync, vsync;

189
src/assembler.js Normal file
View File

@ -0,0 +1,189 @@
EXAMPLE_SPEC = {
vars:{
reg:{bits:2, toks:['a', 'b', 'ip', 'none']},
unop:{bits:3, toks:['mova','movb','inc','dec','asl','lsr','rol','ror']},
binop:{bits:3, toks:['or','and','xor','zero','add','sub','adc','sbb']},
imm4:{bits:4},
imm8:{bits:8},
rel:{bits:8, iprel:true, ipofs:1},
},
rules:[
{fmt:'~binop ~reg,b', bits:['00',1,'1',0]},
{fmt:'~binop ~reg,#~imm8', bits:['01',1,'1',0,2]},
{fmt:'~binop ~reg,[b]', bits:['11',1,'1',0]},
{fmt:'~unop ~reg', bits:['00',1,'0',0]},
{fmt:'lda #~imm8', bits:['01','00','0001',0]},
{fmt:'ldb #~imm8', bits:['01','01','0001',0]},
{fmt:'jmp ~imm8', bits:['01','10','0001',0]},
{fmt:'sta ~imm4', bits:['1001',0]},
{fmt:'bcc ~rel', bits:['1010','0001',0]},
{fmt:'bcs ~rel', bits:['1010','0011',0]},
{fmt:'bz ~rel', bits:['1010','1101',0]},
{fmt:'bnz ~rel', bits:['1010','0101',0]},
{fmt:'clc', bits:['10001000']},
{fmt:'swapab', bits:['10000001']},
{fmt:'reset', bits:['10001111']},
]
}
function rule2regex(rule, vars) {
var s = rule.fmt;
var varlist = [];
rule.prefix = s.split(/\s+/)[0];
s = s.replace(/\s+/g, '\\s');
s = s.replace(/\[/g, '\\[');
s = s.replace(/\]/g, '\\]');
s = s.replace(/\./g, '\\.');
s = s.replace(/~\w+/g, function(varname) {
varname = varname.substr(1);
var v = vars[varname];
varlist.push(varname);
if (!v)
throw Error("Could not find rule for ~" + varname);
else if (v.toks)
return '(\\w+)';
else
return '([0-9]+|[$][0-9a-f]+|\\w+)';
});
rule.re = new RegExp('^'+s+'$', 'i');
rule.varlist = varlist;
// TODO: check rule constraints
return rule;
}
var Assembler = function(spec) {
var self = this;
var ip = 0;
var linenum = 0;
var symbols = {};
var errors = [];
var outwords = [];
var fixups = [];
var width = 8;
for (var i=0; i<spec.rules.length; i++)
rule2regex(spec.rules[i], spec.vars);
function warning(msg) {
errors.push({msg:msg, line:linenum});
}
function addBytes(result) {
var op = result.opcode;
var nb = result.nbits/width;
for (var i=0; i<nb; i++)
outwords[ip++] = (op >> (nb-1-i)*width) & ((1<<width)-1);
}
self.buildInstruction = function(rule, m) {
var opcode = 0;
var oplen = 0;
for (var i=0; i<rule.bits.length; i++) {
var b = rule.bits[i];
var n,x;
if (b.length) {
n = b.length;
x = parseInt(b,2);
} else {
var id = m[b+1];
var v = spec.vars[rule.varlist[b]];
n = v.bits;
if (v.toks) {
x = v.toks.indexOf(id);
if (x < 0)
return null;
} else {
if (id.startsWith("$"))
x = parseInt(id.substr(1), 16);
else
x = parseInt(id);
// is it a label? add fixup
if (isNaN(x)) {
fixups.push({sym:id, ofs:ip, bitlen:n, bitofs:oplen, line:linenum, iprel:!!v.iprel, ipofs:(v.ipofs+0)});
x = 0;
}
}
}
var mask = (1<<n)-1;
if ((x&mask) != x)
warning("Value " + x + " could not fit in " + n + " bits");
opcode = (opcode << n) | x;
oplen += n;
}
if (oplen == 0)
warning("Opcode had zero length");
else if ((oplen % width) != 0)
warning("Opcode was not word-aligned (" + oplen + " bits)");
return {opcode:opcode, nbits:oplen};
}
self.assemble = function(line) {
linenum++;
// remove comments
line = line.replace(/[;].*/g, '');
line = line.trim().toLowerCase();
// is it a directive?
if (line[0] == '.') {
var tokens = line.split(/\s+/);
if (tokens[0] == '.define')
symbols[tokens[1]] = {value:tokens[2]};
else
warning("Unrecognized directive: " + line);
return;
}
// find labels
line = line.replace(/(\w+):/, function(_label, label) {
symbols[label] = {value:ip};
return '';
});
line = line.trim();
// look at each rule in order
for (var i=0; i<spec.rules.length; i++) {
var rule = spec.rules[i];
var m = rule.re.exec(line);
if (m) {
var result = self.buildInstruction(rule, m);
if (result) {
addBytes(result);
return result;
}
}
}
warning("Could not decode instruction: " + line);
}
self.finish = function() {
for (var i=0; i<fixups.length; i++) {
var fix = fixups[i];
var sym = symbols[fix.sym];
if (sym) {
var ofs = fix.ofs + (fix.bitofs>>3);
var shift = fix.bitofs&7;
var mask = ((1<<fix.bitlen)-1);
var value = sym.value;
if (fix.iprel) value -= fix.ofs + fix.ipofs;
value &= mask;
// TODO: check range
// TODO: span multiple words?
outwords[ofs] ^= value;
} else {
warning("Symbol '" + fix.sym + "' not found");
}
}
fixups = [];
}
self.state = function() {
return {ip:ip, line:linenum, output:outwords, errors:errors, fixups:fixups};
}
}
var asm = new Assembler(EXAMPLE_SPEC);
console.log(EXAMPLE_SPEC);
console.log(asm.assemble(".define FOO 0xa")); // TODO
console.log(asm.assemble(" sta FOO"));
console.log(asm.assemble(" sta 10"));
console.log(asm.assemble(" add a,#25 ; comment "));
console.log(asm.assemble("Label: asl a "));
console.log(asm.assemble(" sub b,[b] "));
console.log(asm.assemble(" bz Label "));
asm.finish();
console.log(asm.state());

View File

@ -16,10 +16,11 @@ var VERILOG_PRESETS = [
{id:'sprite_rotation.v', name:'Sprite Rotation'},
{id:'tank.v', name:'Tank Game'},
{id:'cpu8.v', name:'Simple 8-Bit CPU'},
{id:'race_game_cpu.v', name:'Race Game With CPU'},
{id:'racing_game_cpu.v', name:'Racing Game With CPU'},
{id:'music.v', name:'3-Voice Music'},
{id:'lfsr.v', name:'Linear Feedback Shift Register'},
{id:'starfield.v', name:'Scrolling Starfield'},
{id:'framebuffer.v', name:'Frame Buffer'},
];
var VERILOG_KEYCODE_MAP = makeKeycodeMap([

View File

@ -1093,7 +1093,8 @@ function compileCASPR(code, platform, options) {
});
var FS = caspr_mod['FS'];
FS.writeFile("main.asm", code);
var deps = [{prefix:'verilog',filename:'nano8.cfg'}]; // TODO
var arch = code.match(/^[.]arch\s+(\w+)/m);
var deps = [{prefix:'verilog',filename:arch[1]+'.cfg'}]; // TODO: parse file for ".arch femto8"
writeDependencies(deps, FS, errors);
try {
starttime();
@ -1117,6 +1118,7 @@ function compileVerilator(code, platform, options) {
load("verilator2js");
var errors = [];
// compile inline asm
// TODO: keep line numbers
code = code.replace(/__asm\b([\s\S]+?)\b__endasm\b/g, function(s,asmcode) {
var asmout = compileCASPR(asmcode, platform, options);
if (asmout.errors && asmout.errors.length) {
@ -1145,7 +1147,7 @@ function compileVerilator(code, platform, options) {
FS.writeFile(topmod+".v", code);
writeDependencies(options.dependencies, FS, errors);
starttime();
verilator_mod.callMain(["--cc", "-O3",
verilator_mod.callMain(["--cc", "-O3", "-DEXT_INLINE_ASM",
"-Wall", "-Wno-DECLFILENAME", "-Wno-UNUSED", '--report-unoptflat',
"--x-assign", "fast", "--noassert", "--pins-bv", "33",
"--top-module", topmod, topmod+".v"]);