1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2025-03-25 03:33:29 +00:00

moved fpga examples to https://github.com/sehugg/fpga-examples; new framebuffer.v

This commit is contained in:
Steven Hugg 2018-11-12 14:13:17 -05:00
parent d5154a649a
commit bf2250310b
30 changed files with 35 additions and 4892 deletions

View File

@ -74,6 +74,7 @@ TODO:
- update bootstrap/jquery
- clean BOM from verilog
- $readmemb/h
- maybe don't have grey space with line numbers until inline ASM used?
WEB WORKER FORMAT

View File

@ -1,6 +0,0 @@
*.asc
*.bin
*.blif
*.log
*.out
*.vcd

View File

@ -1,22 +0,0 @@
%.asc: %.v icestick.pcf
yosys -p "synth_ice40 -blif $*.blif" $*.v | tee $*.log
arachne-pnr -d 1k -p icestick.pcf $*.blif -o $*.asc
%.bin: %.asc
icetime -c 12 -d hx1k $*.asc
icepack $*.asc $*.bin
%.prog: %.bin
iceprog $<
%.hex: %.asm
node ../../src/tools/jsasm.js < $< > $@
%.vlog: %.asc
icebox_vlog $*.asc > $*.vlog
%.vcd: %.vlog test.vlog
iverilog -o $*.out -v /usr/share/yosys/ice40/cells_sim.v $< test.vlog
vvp $*.out

View File

@ -1,250 +0,0 @@
`include "hvsync_generator.v"
`include "digits10.v"
`include "scoreboard.v"
/*
A brick-smashing ball-and-paddle game.
*/
module ball_paddle_top(clk, reset, hpaddle, out);
input clk;
input reset;
input hpaddle;
wire hsync, vsync;
output [1:0] out;
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
reg [8:0] paddle_pos; // paddle X position
reg [8:0] ball_x; // ball X position
reg [8:0] ball_y; // ball Y position
reg ball_dir_x; // ball X direction (0=left, 1=right)
reg ball_speed_x; // ball speed (0=1 pixel/frame, 1=2 pixels/frame)
reg ball_dir_y; // ball Y direction (0=up, 1=down)
reg brick_array [0:BRICKS_H*BRICKS_V-1]; // 16*8 = 128 bits
wire [3:0] score0; // score right digit
wire [3:0] score1; // score left digit
wire [3:0] lives; // # lives remaining
reg incscore; // incscore signal
reg declives = 0; // TODO
localparam BRICKS_H = 16; // # of bricks across
localparam BRICKS_V = 8; // # of bricks down
localparam BALL_DIR_LEFT = 0;
localparam BALL_DIR_RIGHT = 1;
localparam BALL_DIR_DOWN = 1;
localparam BALL_DIR_UP = 0;
localparam PADDLE_WIDTH = 31; // horizontal paddle size
localparam BALL_SIZE = 6; // square ball size
reg clk2;
always @(posedge clk) begin
clk2 <= !clk2;
end
hvsync_generator #(256,60,40,25) hvsync_gen(
.clk(clk2),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
.hpos(hpos),
.vpos(vpos)
);
// scoreboard
wire score_gfx; // output from score generator
player_stats stats(
.reset(reset),
.score0(score0),
.score1(score1),
.incscore(incscore),
.lives(lives),
.declives(declives)
);
scoreboard_generator score_gen(
.score0(score0),
.score1(score1),
.lives(lives),
.vpos(vpos),
.hpos(hpos),
.board_gfx(score_gfx)
);
wire [5:0] hcell = hpos[8:3]; // horizontal brick index
wire [5:0] vcell = vpos[8:3]; // vertical brick index
wire lr_border = hcell==0 || hcell==31; // along horizontal border?
// TODO: unsigned compare doesn't work in JS
wire [8:0] paddle_rel_x = ((hpos-paddle_pos) & 9'h1ff);
// player paddle graphics signal
wire paddle_gfx = (vcell == 28) && (paddle_rel_x < PADDLE_WIDTH);
// difference between ball position and video beam
wire [8:0] ball_rel_x = (hpos - ball_x);
wire [8:0] ball_rel_y = (vpos - ball_y);
// ball graphics signal
wire ball_gfx = ball_rel_x < BALL_SIZE
&& ball_rel_y < BALL_SIZE;
reg main_gfx; // main graphics signal (bricks and borders)
reg brick_present; // 1 when we are drawing a brick
reg [6:0] brick_index;// index into array of current brick
// brick graphics signal
wire brick_gfx = lr_border || (brick_present && vpos[2:0] != 0 && hpos[3:1] != 4);
// scan bricks: compute brick_index and brick_present flag
always @(posedge clk2)
// see if we are scanning brick area
if (vpos[8:6] == 1 && !lr_border)
begin
// every 16th pixel, starting at 8
if (hpos[3:0] == 8) begin
// compute brick index
brick_index <= {vpos[5:3], hpos[7:4]};
end
// every 17th pixel
else if (hpos[3:0] == 9) begin
// load brick bit from array
brick_present <= !brick_array[brick_index];
end
end else begin
brick_present <= 0;
end
// only works when paddle at bottom of screen!
// (we don't want to mess w/ paddle position during visible portion)
always @(posedge hsync)
if (!hpaddle)
paddle_pos <= vpos;
// 1 when ball signal intersects main (brick + border) signal
wire ball_pixel_collide = main_gfx & ball_gfx;
reg ball_collide_paddle = 0;
reg [3:0] ball_collide_bits = 0;
// compute ball collisions with paddle and playfield
always @(posedge clk2)
// clear all collide bits for frame
if (vsync) begin
ball_collide_bits <= 0;
ball_collide_paddle <= 0;
end else begin
if (ball_pixel_collide) begin
// did we collide w/ paddle?
if (paddle_gfx) begin
ball_collide_paddle <= 1;
end
// ball has 4 collision quadrants
if (!ball_rel_x[2] & !ball_rel_y[2]) ball_collide_bits[0] <= 1;
if (ball_rel_x[2] & !ball_rel_y[2]) ball_collide_bits[1] <= 1;
if (!ball_rel_x[2] & ball_rel_y[2]) ball_collide_bits[2] <= 1;
if (ball_rel_x[2] & ball_rel_y[2]) ball_collide_bits[3] <= 1;
end
end
// compute ball collisions with brick and increment score
always @(posedge clk2)
if (ball_pixel_collide && brick_present) begin
brick_array[brick_index] <= 1;
incscore <= 1; // increment score
end else begin
incscore <= 0; // reset incscore
end
// computes position of ball in relation to center of paddle
wire signed [8:0] ball_paddle_dx = ball_x - paddle_pos + 8;
// ball bounce: determine new velocity/direction
always @(posedge vsync or posedge reset)
begin
if (reset) begin
ball_dir_y <= BALL_DIR_DOWN;
end else
// ball collided with paddle?
if (ball_collide_paddle) begin
// bounces upward off of paddle
ball_dir_y <= BALL_DIR_UP;
// which side of paddle, left/right?
ball_dir_x <= (ball_paddle_dx < 20) ? BALL_DIR_LEFT : BALL_DIR_RIGHT;
// hitting with edge of paddle makes it fast
ball_speed_x <= ball_collide_bits[3:0] != 4'b1100;
end else begin
// collided with playfield
// TODO: can still slip through corners
// compute left/right bounce
casez (ball_collide_bits[3:0])
4'b01?1: ball_dir_x <= BALL_DIR_RIGHT; // left edge/corner
4'b1101: ball_dir_x <= BALL_DIR_RIGHT; // left corner
4'b101?: ball_dir_x <= BALL_DIR_LEFT; // right edge/corner
4'b1110: ball_dir_x <= BALL_DIR_LEFT; // right corner
default: ;
endcase
// compute top/bottom bounce
casez (ball_collide_bits[3:0])
4'b1011: ball_dir_y <= BALL_DIR_DOWN;
4'b0111: ball_dir_y <= BALL_DIR_DOWN;
4'b001?: ball_dir_y <= BALL_DIR_DOWN;
4'b0001: ball_dir_y <= BALL_DIR_DOWN;
4'b0100: ball_dir_y <= BALL_DIR_UP;
4'b1?00: ball_dir_y <= BALL_DIR_UP;
4'b1101: ball_dir_y <= BALL_DIR_UP;
4'b1110: ball_dir_y <= BALL_DIR_UP;
default: ;
endcase
end
end
// ball motion: update ball position
always @(negedge vsync or posedge reset)
begin
if (reset) begin
// reset ball position to top center
ball_x <= 128;
ball_y <= 180;
end else begin
// move ball horizontal and vertical position
if (ball_dir_x == BALL_DIR_RIGHT)
ball_x <= ball_x + (ball_speed_x?1:0) + 1;
else
ball_x <= ball_x - (ball_speed_x?1:0) - 1;
ball_y <= ball_y + (ball_dir_y==BALL_DIR_DOWN?1:-1);
end
end
// compute main_gfx
always @(*)
begin
case (vpos[8:3])
0,1,2: main_gfx = score_gfx; // scoreboard
3: main_gfx = 0;
4: main_gfx = 1; // top border
8,9,10,11,12,13,14,15: main_gfx = brick_gfx; // brick rows 1-8
28: main_gfx = paddle_gfx | lr_border; // paddle
29: main_gfx = hpos[0] ^ vpos[0]; // bottom border
default: main_gfx = lr_border; // left/right borders
endcase
end
// combine signals to RGB output
wire r = display_on && (ball_gfx | paddle_gfx);
wire g = display_on && (main_gfx | ball_gfx);
wire b = display_on && (ball_gfx | brick_present);
assign out = (hsync||vsync) ? 0 : (1+g+(r|b));
endmodule

View File

@ -1,80 +0,0 @@
`include "hvsync_generator.v"
`include "digits10.v"
`include "ram.v"
`include "lfsr.v"
module test_ram2_top(
input clk, reset,
output [1:0] out
);
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
reg ram_writeenable = 0;
wire [9:0] ram_addr = {row,col};
reg [7:0] ram_write;
reg [7:0] ram_read;
reg [7:0] ram_write;
reg [7:0] rand;
reg clk2;
always @(posedge clk) begin
clk2 <= !clk2;
end
RAM_sync ram(
.clk(clk2),
.din(ram_write),
.dout(ram_read),
.addr(ram_addr),
.we(ram_writeenable)
);
LFSR lfsr(
.clk(clk2),
.reset(reset),
.enable(!reset),
.lfsr(rand)
);
hvsync_generator #(256,60,40,25) hvsync_gen(
.clk(clk2),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
.hpos(hpos),
.vpos(vpos)
);
wire [4:0] row = vpos[7:3];
wire [4:0] col = hpos[7:3];
wire [3:0] digit = ram_read[3:0];
wire [2:0] xofs = hpos[2:0];
wire [2:0] yofs = vpos[2:0];
wire [7:0] bits; // TODO?
digits10_case numbers(
.digit(digit),
.yofs(yofs),
.bits(bits)
);
wire g = display_on && bits[xofs ^ 3'b111];
assign out = (hsync||vsync) ? 0 : (1+g+g);
always @(posedge clk2)
if (display_on && vpos[2:0] == 7 && rand[0])
case (hpos[2:0])
6: begin
ram_write <= ram_read + 1;
ram_writeenable <= 1;
end
7: ram_writeenable <= 0;
endcase
endmodule

File diff suppressed because it is too large Load Diff

View File

@ -1,232 +0,0 @@
`ifndef ALU_H
`define ALU_H
// ALU operations
`define OP_ZERO 4'h0
`define OP_LOAD_A 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_LOAD_B 4'hb
`define OP_ADD 4'hc
`define OP_SUB 4'hd
`define OP_ADC 4'he
`define OP_SBB 4'hf
// ALU module
module ALU(A, B, carry, aluop, Y);
parameter N = 8; // default width = 8 bits
input [N-1:0] A; // A input
input [N-1:0] B; // B input
input carry; // carry input
input [3:0] aluop; // alu operation
output [N:0] Y; // Y output + carry
always @(*)
case (aluop)
// unary operations
`OP_ZERO: Y = 0;
`OP_LOAD_A: Y = {1'b0, A};
`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_LOAD_B: Y = {1'b0, B};
// 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
/*
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 (@ operator)
nnnn = 4-bit constant
tttt = flags test for conditional branch
*/
// destinations for COMPUTE instructions
`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, (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(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;
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 B_or_data = 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(B_or_data ? data_in : B),
.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
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; // (only use opcode next cycle)
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 [nnnn]
8'b1001????: begin
address <= {4'b0, data_in[3:0]};
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 (4-7,12-15)
if (aluop[2]) carry <= Y[8];
// set zero flag
zero <= ~|Y[7:0];
// 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
`endif

View File

@ -1,52 +0,0 @@
`include "hvsync_generator.v"
/*
A simple test pattern using the hvsync_generator module.
12000000/15734/2
381.33977373840091521545
381-256-23-7 = 95
*/
module test_hvsync_top(clk, reset, out, led);
input clk, reset;
output [1:0] out;
wire hsync;
wire vsync;
wire [8:0] hpos;
wire [8:0] vpos;
reg [5:0] frame;
output led;
reg clk2;
always @(posedge clk) begin
clk2 <= !clk2;
end
hvsync_generator #(256,60,40,25) hvsync_gen(
.clk(clk2),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
.hpos(hpos),
.vpos(vpos)
);
wire r = display_on && (((hpos&7)==0) || (((vpos+frame)&7)==0));
wire g = display_on && vpos[4];
wire b = display_on && hpos[4];
assign out = (hsync||vsync) ? 0 : (1+g+(r|b));
always @(posedge vsync) begin
frame <= frame + 1;
end
assign led = frame[5];
endmodule
// TODO: PWM grey scales

View File

@ -1,207 +0,0 @@
`ifndef DIGITS10_H
`define DIGITS10_H
`include "hvsync_generator.v"
/*
ROM module with 5x5 bitmaps for the digits 0-9.
digits10_case - Uses the case statement.
digits10_array - Uses an array and initial block.
These two modules are functionally equivalent.
*/
// module for 10-digit bitmap ROM
module digits10_case(digit, yofs, bits);
input [3:0] digit; // digit 0-9
input [2:0] yofs; // vertical offset (0-4)
output reg [4:0] bits; // output (5 bits)
// combine {digit,yofs} into single ROM address
wire [6:0] caseexpr = {digit,yofs};
always @(*)
case (caseexpr)/*{w:5,h:5,count:10}*/
7'o00: bits = 5'b11111;
7'o01: bits = 5'b10001;
7'o02: bits = 5'b10001;
7'o03: bits = 5'b10001;
7'o04: bits = 5'b11111;
7'o10: bits = 5'b01100;
7'o11: bits = 5'b00100;
7'o12: bits = 5'b00100;
7'o13: bits = 5'b00100;
7'o14: bits = 5'b11111;
7'o20: bits = 5'b11111;
7'o21: bits = 5'b00001;
7'o22: bits = 5'b11111;
7'o23: bits = 5'b10000;
7'o24: bits = 5'b11111;
7'o30: bits = 5'b11111;
7'o31: bits = 5'b00001;
7'o32: bits = 5'b11111;
7'o33: bits = 5'b00001;
7'o34: bits = 5'b11111;
7'o40: bits = 5'b10001;
7'o41: bits = 5'b10001;
7'o42: bits = 5'b11111;
7'o43: bits = 5'b00001;
7'o44: bits = 5'b00001;
7'o50: bits = 5'b11111;
7'o51: bits = 5'b10000;
7'o52: bits = 5'b11111;
7'o53: bits = 5'b00001;
7'o54: bits = 5'b11111;
7'o60: bits = 5'b11111;
7'o61: bits = 5'b10000;
7'o62: bits = 5'b11111;
7'o63: bits = 5'b10001;
7'o64: bits = 5'b11111;
7'o70: bits = 5'b11111;
7'o71: bits = 5'b00001;
7'o72: bits = 5'b00001;
7'o73: bits = 5'b00001;
7'o74: bits = 5'b00001;
7'o100: bits = 5'b11111;
7'o101: bits = 5'b10001;
7'o102: bits = 5'b11111;
7'o103: bits = 5'b10001;
7'o104: bits = 5'b11111;
7'o110: bits = 5'b11111;
7'o111: bits = 5'b10001;
7'o112: bits = 5'b11111;
7'o113: bits = 5'b00001;
7'o114: bits = 5'b11111;
default: bits = 0;
endcase
endmodule
module digits10_array(digit, yofs, bits);
input [3:0] digit; // digit 0-9
input [2:0] yofs; // vertical offset (0-4)
output [4:0] bits; // output (5 bits)
reg [4:0] bitarray[16][5]; // ROM array (16 x 5 x 5 bits)
assign bits = bitarray[digit][yofs]; // assign module output
integer i,j;
initial begin/*{w:5,h:5,count:10}*/
bitarray[0][0] = 5'b11111;
bitarray[0][1] = 5'b10001;
bitarray[0][2] = 5'b10001;
bitarray[0][3] = 5'b10001;
bitarray[0][4] = 5'b11111;
bitarray[1][0] = 5'b01100;
bitarray[1][1] = 5'b00100;
bitarray[1][2] = 5'b00100;
bitarray[1][3] = 5'b00100;
bitarray[1][4] = 5'b11111;
bitarray[2][0] = 5'b11111;
bitarray[2][1] = 5'b00001;
bitarray[2][2] = 5'b11111;
bitarray[2][3] = 5'b10000;
bitarray[2][4] = 5'b11111;
bitarray[3][0] = 5'b11111;
bitarray[3][1] = 5'b00001;
bitarray[3][2] = 5'b11111;
bitarray[3][3] = 5'b00001;
bitarray[3][4] = 5'b11111;
bitarray[4][0] = 5'b10001;
bitarray[4][1] = 5'b10001;
bitarray[4][2] = 5'b11111;
bitarray[4][3] = 5'b00001;
bitarray[4][4] = 5'b00001;
bitarray[5][0] = 5'b11111;
bitarray[5][1] = 5'b10000;
bitarray[5][2] = 5'b11111;
bitarray[5][3] = 5'b00001;
bitarray[5][4] = 5'b11111;
bitarray[6][0] = 5'b11111;
bitarray[6][1] = 5'b10000;
bitarray[6][2] = 5'b11111;
bitarray[6][3] = 5'b10001;
bitarray[6][4] = 5'b11111;
bitarray[7][0] = 5'b11111;
bitarray[7][1] = 5'b00001;
bitarray[7][2] = 5'b00001;
bitarray[7][3] = 5'b00001;
bitarray[7][4] = 5'b00001;
bitarray[8][0] = 5'b11111;
bitarray[8][1] = 5'b10001;
bitarray[8][2] = 5'b11111;
bitarray[8][3] = 5'b10001;
bitarray[8][4] = 5'b11111;
bitarray[9][0] = 5'b11111;
bitarray[9][1] = 5'b10001;
bitarray[9][2] = 5'b11111;
bitarray[9][3] = 5'b00001;
bitarray[9][4] = 5'b11111;
end
endmodule
// test module
module test_numbers_top(clk, reset, hsync, vsync, rgb);
input clk, reset;
output hsync, vsync;
output [2:0] rgb;
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
hvsync_generator hvsync_gen(
.clk(clk),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
.hpos(hpos),
.vpos(vpos)
);
wire [3:0] digit = hpos[7:4];
wire [2:0] xofs = hpos[3:1];
wire [2:0] yofs = vpos[3:1];
wire [4:0] bits;
digits10_array numbers(
.digit(digit),
.yofs(yofs),
.bits(bits)
);
wire r = display_on && 0;
wire g = display_on && bits[xofs ^ 3'b111];
wire b = display_on && 0;
assign rgb = {b,g,r};
endmodule
`endif

View File

@ -1,21 +0,0 @@
`ifndef FONT_CP437_H
`define FONT_CP437_H
// PC font (code page 437)
module font_cp437_8x8(addr, data);
input [10:0] addr;
output [7:0] data;
reg [7:0] bitarray[0:2047];
assign data = bitarray[addr];
initial begin/*{w:8,h:8,bpp:1,count:256}*/
$readmemh("cp437.hex", bitarray);
end
endmodule
`endif

View File

@ -1,69 +0,0 @@
`ifndef HVSYNC_GENERATOR_H
`define HVSYNC_GENERATOR_H
/*
Video sync generator, used to drive a simulated CRT.
To use:
- Wire the hsync and vsync signals to top level outputs
- Add a 3-bit (or more) "rgb" output to the top level
*/
module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);
input clk;
input reset;
output reg hsync, vsync;
output display_on;
output reg [8:0] hpos;
output reg [8:0] vpos;
// declarations for TV-simulator sync parameters
// horizontal constants
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 constants
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 constants
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; // set when hpos is maximum
wire vmaxxed = (vpos == V_MAX) || reset; // set when vpos is maximum
// horizontal position counter
always @(posedge clk)
begin
hsync <= (hpos>=H_SYNC_START && hpos<=H_SYNC_END);
if(hmaxxed)
hpos <= 0;
else
hpos <= hpos + 1;
end
// vertical position counter
always @(posedge clk)
begin
vsync <= (vpos>=V_SYNC_START && vpos<=V_SYNC_END);
if(hmaxxed)
if (vmaxxed)
vpos <= 0;
else
vpos <= vpos + 1;
end
// display_on is set when beam is in "safe" visible frame
assign display_on = (hpos<H_DISPLAY) && (vpos<V_DISPLAY);
endmodule
`endif

View File

@ -1,16 +0,0 @@
# full iCEstick pinout:
# http://www.pighixxx.com/test/portfolio-items/icestick/
#set_io --warn-no-port RX 9
#set_io --warn-no-port TX 8
#set_io LED1 99
#set_io LED5 95
set_io clk 21
set_io out[0] 80
set_io out[1] 81
set_io --warn-no-port hpaddle 79
set_io --warn-no-port vpaddle 78

View File

@ -1,31 +0,0 @@
`ifndef LFSR_V
`define LFSR_V
/*
Configurable Linear Feedback Shift Register.
*/
module LFSR(clk, reset, enable, lfsr);
parameter NBITS = 8; // bit width
parameter TAPS = 8'b11101; // bitmask for taps
parameter INVERT = 0; // invert feedback bit?
input clk, reset;
input enable; // only perform shift when enable=1
output reg [NBITS-1:0] lfsr; // shift register
wire feedback = lfsr[NBITS-1] ^ INVERT;
always @(posedge clk)
begin
if (reset)
lfsr <= {lfsr[NBITS-2:0], 1'b1}; // reset loads with all 1s
else if (enable)
lfsr <= {lfsr[NBITS-2:0], 1'b0} ^ (feedback ? TAPS : 0);
end
endmodule
`endif

View File

@ -1,111 +0,0 @@
.arch ../../presets/verilog/femto8 ; FEMTO-8 architecture
.org 128 ; origin = 128 ($80)
.len 128 ; length = 128 ($80)
; define constants
.define PADDLE_X 0
.define PADDLE_Y 1
.define PLAYER_X 2
.define PLAYER_Y 3
.define ENEMY_X 4
.define ENEMY_Y 5
.define ENEMY_DIR 6
.define SPEED 7
.define TRACKPOS_LO 8
.define TRACKPOS_HI 9
.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
.define F_COLLIDE 32
Start:
lda #128 ; load initial positions
sta PLAYER_X ; player_x = 128
sta ENEMY_X ; enemy_x = 128
sta ENEMY_Y ; enemy_y = 128
lda #180
sta PLAYER_Y ; player_y = 180
zero A
sta SPEED ; player speed = 0
inc A
sta ENEMY_DIR ; enemy dir = 1 (right)
; read horizontal paddle position
DisplayLoop:
; lda #F_HPADDLE ; paddle flag -> A
; ldb #IN_FLAGS ; addr of IN_FLAGS -> B
; and none,[B] ; read B, AND with A
; bz DisplayLoop ; loop until paddle flag set
ldb #IN_VPOS
mov A,[B] ; load vertical position -> A
sta PLAYER_X ; store player x position
; wait for vsync
lda #F_VSYNC
ldb #IN_FLAGS
WaitForVsyncOn:
and none,[B]
bz WaitForVsyncOn ; wait until VSYNC on
WaitForVsyncOff:
and none,[B]
bnz WaitForVsyncOff ; wait until VSYNC off
; check collision
lda #F_COLLIDE
ldb #IN_FLAGS
and none,[B] ; collision flag set?
bz NoCollision ; skip ahead if not
lda #16
sta SPEED ; speed = 16
NoCollision:
; update speed
ldb #SPEED
mov A,[B] ; speed -> A
inc A ; increment speed
bz MaxSpeed ; speed wraps to 0?
sta SPEED ; no, store speed
MaxSpeed:
mov A,[B] ; reload speed -> A
lsr A
lsr A
lsr A
lsr A ; divide speed by 16
; add to lo byte of track pos
ldb #TRACKPOS_LO
add B,[B] ; B <- speed/16 + trackpos_lo
swapab ; swap A <-> B
sta TRACKPOS_LO ; A -> trackpos_lo
swapab ; swap A <-> B again
bcc NoCarry ; carry flag from earlier add op
; add to hi byte of track pos
ldb #TRACKPOS_HI
mov B,[B] ; B <- trackpos_hi
inc b ; increment B
swapab ; swap A <-> B
sta TRACKPOS_HI ; A -> trackpos_hi
swapab ; swap A <-> B again
NoCarry:
; update enemy vert pos
ldb #ENEMY_Y
add A,[B]
sta ENEMY_Y ; enemy_y = enemy_y + speed/16
; update enemy horiz pos
ldb #ENEMY_X
mov A,[B]
ldb #ENEMY_DIR
add A,[B]
sta ENEMY_X ; enemy_x = enemy_x + enemy_dir
sub A,#64
and A,#127 ; A <- (enemy_x-64) & 127
bnz SkipXReverse ; skip if enemy_x is in range
; load ENEMY_DIR and negate
zero A
sub A,[B]
sta ENEMY_DIR ; enemy_dir = -enemy_dir
SkipXReverse:
; back to display loop
jmp DisplayLoop

View File

@ -1,128 +0,0 @@
4b
80
92
94
95
4b
b4
93
0
97
2
96
5b
41
cb
92
4b
10
5b
42
f9
ac
94
f9
a4
97
4b
20
5b
42
f9
ac
a4
4b
10
97
5b
7
cb
2
ac
ab
97
cb
5
5
5
5
5b
8
dc
81
98
81
a1
bf
5b
9
db
12
81
99
81
5b
5
cc
95
5b
4
cb
5b
6
cc
94
4d
40
49
7f
a4
d3
0
cd
96
6b
8c
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

View File

@ -1,158 +0,0 @@
`include "hvsync_generator.v"
`include "sprite_bitmap.v"
`include "sprite_renderer.v"
/*
A simple racing game with two sprites and a scrolling playfield.
This version does not use a CPU; all logic is straight Verilog.
*/
module racing_game_top(clk, reset, out, hpaddle, vpaddle);
input clk, reset;
input hpaddle, vpaddle;
output [1:0] out;
wire hsync, vsync;
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
// player car position (set at VSYNC)
reg [7:0] player_x;
reg [7:0] player_y;
// paddle position (set continuously during frame)
reg [7:0] paddle_x;
reg [7:0] paddle_y;
// enemy car position
reg [7:0] enemy_x = 128;
reg [7:0] enemy_y = 128;
// enemy car direction, 1=right, 0=left
reg enemy_dir = 0;
reg [15:0] track_pos = 0; // player position along track (16 bits)
reg [7:0] speed = 31; // player velocity along track (8 bits)
reg clk2;
always @(posedge clk) begin
clk2 <= !clk2;
end
// video sync generator
hvsync_generator #(256,60,40,25) hvsync_gen(
.clk(clk2),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
.hpos(hpos),
.vpos(vpos)
);
// set paddle registers
always @(posedge hsync)
begin
if (!hpaddle) paddle_x <= vpos[7:0];
if (!vpaddle) paddle_y <= vpos[7:0];
end
// select player or enemy access to ROM
wire player_load = (hpos >= 256) && (hpos < 260);
wire enemy_load = (hpos >= 260);
// wire up car sprite ROM
// multiplex between player and enemy ROM address
wire [3:0] player_sprite_yofs;
wire [3:0] enemy_sprite_yofs;
wire [3:0] car_sprite_yofs = player_load ? player_sprite_yofs : enemy_sprite_yofs;
wire [7:0] car_sprite_bits;
car_bitmap car(
.yofs(car_sprite_yofs),
.bits(car_sprite_bits));
// signals for player sprite generator
wire player_vstart = {1'b0,player_y} == vpos;
wire player_hstart = {1'b0,player_x} == hpos;
wire player_gfx;
wire player_is_drawing;
// signals for enemy sprite generator
wire enemy_vstart = {1'b0,enemy_y} == vpos;
wire enemy_hstart = {1'b0,enemy_x} == hpos;
wire enemy_gfx;
wire enemy_is_drawing;
// player sprite generator
sprite_renderer player_renderer(
.clk(clk2),
.reset(reset),
.vstart(player_vstart),
.load(player_load),
.hstart(player_hstart),
.rom_addr(player_sprite_yofs),
.rom_bits(car_sprite_bits),
.gfx(player_gfx),
.in_progress(player_is_drawing));
// enemy sprite generator
sprite_renderer enemy_renderer(
.clk(clk2),
.reset(reset),
.vstart(enemy_vstart),
.load(enemy_load),
.hstart(enemy_hstart),
.rom_addr(enemy_sprite_yofs),
.rom_bits(car_sprite_bits),
.gfx(enemy_gfx),
.in_progress(enemy_is_drawing));
// signals for enemy bouncing off left/right borders
wire enemy_hit_left = (enemy_x == 64);
wire enemy_hit_right = (enemy_x == 192);
wire enemy_hit_edge = enemy_hit_left || enemy_hit_right;
// update player, enemy, track counters
// runs once per frame
always @(posedge vsync)
begin
player_x <= 64; //paddle_x;
player_y <= 180;
track_pos <= track_pos + {11'b0,speed[7:4]};
enemy_y <= enemy_y + {3'b0, speed[7:4]};
if (enemy_hit_edge)
enemy_dir <= !enemy_dir;
if (enemy_dir ^ enemy_hit_edge)
enemy_x <= enemy_x + 1;
else
enemy_x <= enemy_x - 1;
// collision check?
if (frame_collision)
speed <= 16;
else if (speed < ~paddle_y)
speed <= speed + 1;
else if (speed > 16)
speed <= speed - 1;
end
// set to 1 when player collides with enemy or track
reg frame_collision;
always @(posedge clk2)
if (player_gfx && (enemy_gfx || track_gfx))
frame_collision <= 1;
else if (vsync)
frame_collision <= 0;
// track graphics signals
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]!=track_pos[5:1]) && track_offside;
// combine signals for RGB output
wire r = display_on && (enemy_gfx || track_shoulder);
wire g = display_on && (player_gfx || track_gfx);
wire b = display_on && (player_gfx || track_shoulder);
assign out = (hsync||vsync) ? 0 : (1+g+(r|b));
endmodule

View File

@ -1,169 +0,0 @@
`include "hvsync_generator.v"
`include "sprite_bitmap.v"
`include "sprite_renderer.v"
`include "cpu8.v"
/*
A simple racing game with two sprites and a scrolling playfield.
This version uses the 8-bit CPU.
*/
// uncomment to see scope view
//`define DEBUG
module racing_game_top(clk, reset, out, hpaddle, vpaddle);
input clk, reset;
input hpaddle, vpaddle;
output [1:0] out;
wire hsync, vsync;
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
parameter PADDLE_X = 0; // paddle X coordinate
parameter PADDLE_Y = 1; // paddle Y coordinate
parameter PLAYER_X = 2; // player X coordinate
parameter PLAYER_Y = 3; // player Y coordinate
parameter ENEMY_X = 4; // enemy X coordinate
parameter ENEMY_Y = 5; // enemy Y coordinate
parameter ENEMY_DIR = 6; // enemy direction (1, -1)
parameter SPEED = 7; // player speed
parameter TRACKPOS_LO = 8; // track position (lo byte)
parameter TRACKPOS_HI = 9; // track position (hi byte)
parameter IN_HPOS = 8'h40; // CRT horizontal position
parameter IN_VPOS = 8'h41; // CRT vertical position
// flags: [0, 0, collision, vsync, hsync, vpaddle, hpaddle, display_on]
parameter IN_FLAGS = 8'h42;
reg [7:0] ram[0:15]; // 16 bytes of RAM
reg [7:0] rom[0:127]; // 128 bytes of ROM
wire [7:0] address_bus; // CPU address bus
reg [7:0] to_cpu; // data bus to CPU
wire [7:0] from_cpu; // data bus from CPU
wire write_enable; // write enable bit from CPU
reg clk2;
always @(posedge clk) begin
clk2 <= !clk2;
end
// 8-bit CPU module
CPU cpu(.clk(clk2),
.reset(reset),
.address(address_bus),
.data_in(to_cpu),
.data_out(from_cpu),
.write(write_enable));
// RAM write from CPU
always @(posedge clk2)
if (write_enable)
ram[address_bus[5:0]] <= from_cpu;
// RAM read from CPU
always @(*)
casez (address_bus)
// RAM
8'b00??