1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-07 17:29:31 +00:00

work on simple CPU, paddle game, `include local files too, scope scrolling, hvsync reset

This commit is contained in:
Steven Hugg 2017-11-28 20:48:27 -05:00
parent 80588fcb31
commit d732f320b0
12 changed files with 143 additions and 263 deletions

View File

@ -74,6 +74,7 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
hvsync_generator hvsync_gen(
.clk(clk),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
@ -125,10 +126,11 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
reg main_gfx;
reg brick_present;
reg [6:0] brick_index;
wire brick_gfx = lr_border || (brick_present && vpos[2:0] != 0 && hpos[3:1] != 4);
wire visible_clk = clk & display_on;
// compute main_gfx and locate bricks
// scan bricks: compute brick_index and brick_present flag
always @(posedge visible_clk)
// see if we are scanning brick area
if (vpos[8:6] == 1 && !lr_border)
@ -137,31 +139,19 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
if (hpos[3:0] == 8) begin
// compute brick index
brick_index <= {vpos[5:3], hpos[7:4]};
main_gfx <= 0; // 2 pixel horiz spacing between bricks
//main_gfx <= 0; // 2 pixel horiz spacing between bricks
end
// every 17th pixel
else if (hpos[3:0] == 9) begin
// load brick bit from array
brick_present <= brick_array[brick_index];
end else begin
main_gfx <= brick_present && vpos[2:0] != 0; // 1 pixel vert. spacing
//main_gfx <= brick_present && vpos[2:0] != 0; // 1 pixel vert. spacing
end
end else begin
brick_present <= 0;
case (vpos[8:3])
0: main_gfx <= score_gfx; // scoreboard
1: main_gfx <= score_gfx;
2: main_gfx <= score_gfx;
3: main_gfx <= 0;
4: main_gfx <= 1; // top border
//14: main_gfx <= hpos[4];
//21: main_gfx <= hpos[5];
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
// only works when paddle at bottom of screen!
// (we don't want to mess w/ paddle position during visible portion)
always @(posedge hsync)
@ -171,15 +161,16 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
wire ball_pixel_collide = main_gfx & ball_gfx;
/* verilator lint_off MULTIDRIVEN */
reg [4:0] ball_collide_bits = 0;
reg ball_collide_paddle = 0;
reg [3:0] ball_collide_bits = 0;
/* verilator lint_on MULTIDRIVEN */
// compute ball collisions with paddle and playfield
always @(posedge visible_clk)
if (ball_pixel_collide) begin
// did we collide w/ paddle?
if (paddle_gfx) begin
// did we collide w/ paddle?
ball_collide_bits[4] <= 1; // bit 4 == paddle collide
ball_collide_paddle <= 1;
end
// ball has 4 collision quadrants
if (!ball_rel_x[2] & !ball_rel_y[2]) ball_collide_bits[0] <= 1;
@ -188,7 +179,7 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
if (ball_rel_x[2] & ball_rel_y[2]) ball_collide_bits[3] <= 1;
end
// compute ball collisions with brick
// compute ball collisions with brick and increment score
always @(posedge visible_clk)
if (ball_pixel_collide && brick_present) begin
brick_array[brick_index] <= 0;
@ -199,18 +190,18 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
wire signed [8:0] ball_paddle_dx = ball_x - paddle_pos + 8;
// compute ball new position and velocity
// 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_bits[4]) begin
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 < 16) ? BALL_DIR_LEFT : BALL_DIR_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
@ -238,8 +229,10 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
endcase
end
ball_collide_bits <= 0; // clear all collide bits for frame
ball_collide_paddle <= 0;
end
// ball motion: update ball position
always @(negedge vsync or posedge reset)
begin
if (reset) begin
@ -254,6 +247,29 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
end
end
// compute main_gfx
always @(*)
begin
case (vpos[8:3])
0: main_gfx = score_gfx; // scoreboard
1: main_gfx = score_gfx;
2: main_gfx = score_gfx;
3: main_gfx = 0;
4: main_gfx = 1; // top border
8: main_gfx = brick_gfx; // 1st brick row
9: main_gfx = brick_gfx; // ...
10: main_gfx = brick_gfx;
11: main_gfx = brick_gfx;
12: main_gfx = brick_gfx;
13: main_gfx = brick_gfx;
14: main_gfx = brick_gfx;
15: main_gfx = brick_gfx; // 8th brick row
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
wire grid_gfx = (((hpos&7)==0) || ((vpos&7)==0));
wire r = display_on && (ball_gfx | paddle_gfx);

View File

@ -10,14 +10,17 @@ module ball_paddle_top(clk, reset, hsync, vsync, rgb);
wire [8:0] hpos;
wire [8:0] vpos;
reg [7:0] ball_htimer;
reg [7:0] ball_vtimer;
reg [8:0] ball_htimer;
reg [8:0] ball_vtimer;
reg [3:0] ball_horiz_vel;
reg [3:0] ball_vert_vel;
reg [8:0] ball_horiz_stop = 209;
reg [8:0] ball_horiz_move = -2;
reg [8:0] ball_vert_stop = 251;
reg [8:0] ball_vert_move = 2;
hvsync_generator hvsync_gen(
.clk(clk),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
@ -25,29 +28,56 @@ module ball_paddle_top(clk, reset, hsync, vsync, rgb);
.vpos(vpos)
);
always @(posedge clk)
// update horizontal timer
always @(posedge clk or posedge reset)
begin
if (hpos == 0 && vpos == 0)
ball_htimer <= ball_htimer + 8'(ball_horiz_vel) - 4;
else if (display_on)
if (reset)
ball_htimer <= ball_horiz_stop - 128;
else if (ball_htimer == 0) begin
if (ball_vtimer == 0)
ball_htimer <= ball_horiz_stop + ball_horiz_move;
else
ball_htimer <= ball_horiz_stop;
end else
ball_htimer <= ball_htimer + 1;
end;
always @(posedge hsync)
// update vertical timer
always @(posedge hsync or posedge reset)
begin
if (vpos > 9'(ball_vert_vel))
if (reset)
ball_vtimer <= ball_vert_stop - 128;
else if (ball_vtimer == 0)
ball_vtimer <= ball_vert_stop + ball_vert_move;
else
ball_vtimer <= ball_vtimer + 1;
end;
wire ball_hgfx = ball_htimer < 8;
wire ball_vgfx = ball_vtimer < 8;
wire ball_gfx = ball_hgfx & ball_vgfx;
wire grid_gfx = (((hpos&7)==0) || ((vpos&7)==0));
// vertical bounce
always @(posedge ball_vert_collide)
begin
ball_vert_move <= -ball_vert_move;
end;
wire r = display_on && (grid_gfx | ball_gfx);
wire g = display_on && ball_gfx;
wire b = display_on && ball_gfx;
// horizontal bounce
always @(posedge ball_horiz_collide)
begin
ball_horiz_move <= -ball_horiz_move;
end;
wire ball_hgfx = ball_htimer >= 508;
wire ball_vgfx = ball_vtimer >= 508;
wire ball_gfx = ball_hgfx && ball_vgfx;
// collide with vertical and horizontal boundaries
wire ball_vert_collide = ball_vgfx && vpos >= 240;
wire ball_horiz_collide = ball_hgfx && hpos >= 256 && vpos == 255;
wire grid_gfx = (((hpos&7)==0) && ((vpos&7)==0));
wire r = display_on && (ball_hgfx | ball_gfx);
wire g = display_on && (grid_gfx | ball_gfx);
wire b = display_on && (ball_vgfx | ball_gfx);
assign rgb = {b,g,r};
endmodule

View File

@ -82,7 +82,7 @@ module digits10_array(digit, yofs, bits);
reg [4:0] bitarray[10][5];
assign bits = bitarray[digit][yofs];
initial begin/*{w:5,h:5,count:10}*/
bitarray[0][0] = 5'b11111;
bitarray[0][1] = 5'b10001;
@ -157,6 +157,7 @@ module test_numbers_top(clk, hsync, vsync, rgb);
hvsync_generator hvsync_gen(
.clk(clk),
.reset(0),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),

View File

@ -2,15 +2,16 @@
`define HVSYNC_GENERATOR_H
module hvsync_generator(
clk, hsync, vsync, display_on, hpos, vpos);
clk, reset, hsync, vsync, display_on, hpos, vpos);
input clk;
output hsync, vsync;
input reset;
output reg hsync, vsync;
output reg display_on;
output reg [8:0] hpos;
output reg [8:0] vpos;
// constant declarations for VGA sync parameters
// constant declarations for TV-simulator sync parameters
localparam H_DISPLAY = 256; // horizontal display area
localparam H_L_BORDER = 16; // horizontal left border
localparam H_R_BORDER = 16; // horizontal right border
@ -27,15 +28,17 @@ module hvsync_generator(
localparam START_V_RETRACE = V_DISPLAY + V_B_BORDER;
localparam END_V_RETRACE = V_DISPLAY + V_B_BORDER + V_RETRACE - 1;
wire hmaxxed = (hpos==H_MAX);
wire vmaxxed = (vpos==V_MAX);
always @(posedge clk)
wire hmaxxed = (hpos == H_MAX) || reset;
wire vmaxxed = (vpos == V_MAX) || reset;
// increment horizontal position counter
always @(posedge clk)
if(hmaxxed)
hpos <= 0;
else
hpos <= hpos + 1;
// increment vertical position counter
always @(posedge clk)
if(hmaxxed)
if (!vmaxxed)
@ -43,14 +46,11 @@ module hvsync_generator(
else
vpos <= 0;
// compute hsync + vsync + display_on signals
always @(posedge clk)
begin
hsync <= (hpos>=START_H_RETRACE && hpos<=END_H_RETRACE);
vsync <= (vpos==START_V_RETRACE);
end
always @(posedge clk)
begin
display_on <= (hpos<H_DISPLAY) && (vpos<V_DISPLAY);
end

View File

@ -1,196 +0,0 @@
`define OP_LOAD_A 4'h0
`define OP_LOAD_B 4'h1
`define OP_ADD 4'h2
`define OP_SUB 4'h3
`define OP_INC 4'h4
`define OP_DEC 4'h5
`define OP_ASL 4'h6
`define OP_LSR 4'h7
`define OP_OR 4'h8
`define OP_AND 4'h9
`define OP_XOR 4'ha
module ALU(A, B, Y, aluop);
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_ADD: Y = A + B;
`OP_SUB: Y = A - B;
`OP_INC: Y = A + 1;
`OP_DEC: Y = A - 1;
`OP_ASL: Y = {A[7], A + A};
`OP_LSR: Y = {A[0], A >> 1};
`OP_OR: Y = {1'b0, A | B};
`OP_AND: Y = {1'b0, A & B};
`OP_XOR: Y = {1'b0, A ^ B};
default: Y = 9'bx;
endcase
endmodule
`define REG_A 1'b0
`define REG_B 1'b1
`define I_CONST(r,x) { 2'b00, r, x }
`define I_LOAD_ADDR(r,addr) { 3'b010, r, addr }
`define I_STORE_ADDR(r,addr) { 3'b011, r, addr }
`define I_COMPUTE(r,op) { 3'b100, r, op }
`define I_RESET 8'hff
module CPU(clk, reset, address, data_in, data_out, write);
input clk, reset;
output [7:0] address;
input [7:0] data_in;
output [7:0] data_out;
output write;
reg [7:0] ip;
reg [7:0] opcode;
reg [3:0] aluop;
reg [7:0] A, B;
reg [8:0] Y;
reg [1:0] state;
reg [2:0] action;
reg carry;
reg zero;
localparam NOOP = 0;
localparam MAKE_CONST = 1;
localparam LOAD_ADDR = 2;
localparam STORE_ADDR = 3;
localparam COMPUTE = 4;
wire load_const = opcode[3:1] == 3'b111;
ALU alu(.A(A), .B(B), .Y(Y), .aluop(aluop));
always @(posedge clk)
if (reset) begin
state <= 0;
write <= 0;
end else begin
case (state)
// state 0: reset
0: begin
ip <= 8'h80;
write <= 0;
state <= 1;
end
// state 1: select opcode address
1: begin
address <= ip;
ip <= ip + 1;
write <= 0;
state <= 2;
end
// state 2: read/decode opcode
2: begin
casez (data_in)
8'b00??????: begin
action <= MAKE_CONST;
if (data_in[5])
B <= {3'b0, data_in[4:0]};
else
A <= {3'b0, data_in[4:0]};
state <= 1;
end
8'b010?????: begin
action <= LOAD_ADDR;
address <= {4'b0, data_in[3:0]};
state <= 3;
end
8'b011?????: begin
action <= STORE_ADDR;
address <= {4'b0, data_in[3:0]};
state <= 3;
end
8'b100?????: begin
action <= COMPUTE;
aluop <= data_in[3:0];
state <= 3;
end
default: begin
action <= NOOP;
state <= 0; // reset
end
endcase
opcode <= data_in;
end
// state 3: perform action
3: begin
if (action == LOAD_ADDR) begin
if (opcode[4])
B <= data_in;
else
A <= data_in;
end else if (action == STORE_ADDR) begin
if (opcode[4])
data_out <= B;
else
data_out <= A;
write <= 1;
end else if (action == COMPUTE) begin
if (opcode[4])
B <= Y[7:0];
else
A <= Y[7:0];
carry <= Y[8];
zero <= ~|Y;
end
state <= 1; // repeat loop at state 1
end
endcase
end
endmodule
module test_CPU_top(clk, reset, address_bus, to_cpu, from_cpu, write_enable);
input clk, reset;
output [7:0] address_bus;
output reg [7:0] to_cpu;
output [7:0] from_cpu;
output write_enable;
reg [7:0] ram[128];
reg [7:0] rom[128];
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
// address 0x80
rom['h00] = `I_CONST(`REG_A, 5'h1f);
rom['h01] = `I_COMPUTE(`REG_A, `OP_ASL);
rom['h02] = `I_COMPUTE(`REG_A, `OP_ASL);
rom['h03] = `I_COMPUTE(`REG_A, `OP_ASL);
rom['h04] = `I_COMPUTE(`REG_A, `OP_ASL);
rom['h05] = `I_COMPUTE(`REG_B, `OP_LOAD_A);
rom['h06] = `I_STORE_ADDR(`REG_B, 4'd1);
rom['h07] = `I_LOAD_ADDR(`REG_B, 4'd1);
rom['h08] = `I_RESET;
end
endmodule

View File

@ -29,7 +29,7 @@ module car_bitmap(yofs, bits);
end
endmodule
module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
module sprite_bitmap_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
input clk;
input hpaddle, vpaddle;
@ -49,6 +49,7 @@ module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
hvsync_generator hvsync_gen(
.clk(clk),
.reset(0),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),

View File

@ -24,6 +24,7 @@ module sprite_multiple_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
hvsync_generator hvsync_gen(
.clk(clk),
.reset(0),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),

View File

@ -6,7 +6,7 @@ module car_bitmap(yofs, bits);
output [7:0] bits;
reg [7:0] bitarray[16];
assign bits = bitarray[yofs];
initial begin/*{w:8,h:16}*/
@ -102,6 +102,7 @@ module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
hvsync_generator hvsync_gen(
.clk(clk),
.reset(0),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),

View File

@ -11,6 +11,7 @@ module test_hvsync_top(clk, hsync, vsync, rgb);
hvsync_generator hvsync_gen(
.clk(clk),
.reset(0),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),

View File

@ -11,6 +11,7 @@ var VERILOG_PRESETS = [
{id:'sprite_bitmap.v', name:'Sprite Bitmaps'},
{id:'sprite_renderer.v', name:'Sprite Rendering'},
{id:'sprite_multiple.v', name:'Multiple Sprites'},
{id:'cpu8.v', name:'Simple 8-Bit CPU'},
];
var VERILOG_KEYCODE_MAP = makeKeycodeMap([
@ -170,7 +171,8 @@ var VerilogPlatform = function(mainElement, options) {
var switches = [0];
var inspect_obj, inspect_sym;
var inspect_data = new Uint32Array(videoWidth * videoHeight);
var scope_time_x = 0;
var scope_time_x = 0; // scope cursor
var scope_x_offset = 0;
var scope_y_offset = 0;
var scope_max_y = 0;
@ -289,7 +291,8 @@ var VerilogPlatform = function(mainElement, options) {
var COLOR_BORDER = 0xff662222;
var COLOR_TRANS_SIGNAL = 0xff226622;
var COLOR_BLIP_SIGNAL = 0xff226622;
var j = 0;
var jstart = scope_x_offset * arr.length;
var j = jstart;
for (var x=0; x<videoWidth; x++) {
var yb = 8;
var y1 = scope_y_offset;
@ -315,7 +318,7 @@ var VerilogPlatform = function(mainElement, options) {
y1 += ys+yb;
}
}
scope_max_y = y1;
scope_max_y = y1 - scope_y_offset;
video.updateFrame();
// draw labels
var ctx = video.getContext();
@ -327,12 +330,27 @@ var VerilogPlatform = function(mainElement, options) {
ctx.textAlign = 'left';
ctx.fillText(name, 1, yposlist[i]);
if (scope_time_x > 0) {
ctx.fillRect(scope_time_x, 0, 1, 4000);
ctx.textAlign = 'right';
var value = arr.length * scope_time_x + i;
var value = arr.length * scope_time_x + i + jstart;
ctx.fillText(""+trace_buffer[value], videoWidth-1, yposlist[i]);
}
}
// draw scope line & label
if (scope_time_x > 0) {
ctx.fillStyle = "cyan";
ctx.fillText(""+(scope_time_x+scope_x_offset),
(scope_time_x>10)?(scope_time_x-2):(scope_time_x+20), videoHeight-2);
ctx.fillRect(scope_time_x, 0, 1, 4000);
}
// scroll left/right
if (scope_time_x >= videoWidth && scope_x_offset < (trace_buffer.length / arr.length) - videoWidth) {
scope_x_offset += 1 + (scope_time_x - videoWidth);
dirty = true;
}
else if (scope_time_x < 0 && scope_x_offset > 0) {
scope_x_offset = Math.max(0, scope_x_offset + scope_time_x);
dirty = true;
}
}
function clamp(minv,maxv,v) {
@ -352,7 +370,7 @@ var VerilogPlatform = function(mainElement, options) {
var new_x = Math.floor(e.offsetX * video.canvas.width / $(video.canvas).width() - 20);
var new_y = Math.floor(e.offsetY * video.canvas.height / $(video.canvas).height() - 20);
if (mouse_pressed) {
scope_y_offset = clamp(-scope_max_y, 0, scope_y_offset + new_y - paddle_y);
scope_y_offset = clamp(Math.min(0,-scope_max_y+videoHeight), 0, scope_y_offset + new_y - paddle_y);
scope_time_x = Math.floor(e.offsetX * video.canvas.width / $(video.canvas).width() - 16);
dirty = true;
}
@ -432,7 +450,7 @@ var VerilogPlatform = function(mainElement, options) {
}
this.reset = function() {
gen.__reset();
trace_index = 0;
trace_index = scope_x_offset = 0;
trace_buffer.fill(0);
dirty = true;
}
@ -462,6 +480,7 @@ var VerilogPlatform = function(mainElement, options) {
} else {
inspect_obj = inspect_sym = null;
}
dirty = true;
}
// DEBUGGING

View File

@ -419,12 +419,12 @@ function updateSelector() {
function loadFileDependencies(text) {
var arr = [];
if (platform_id == 'verilog') {
var re = /`include\s+"(.+)"/g;
var re = /`include\s+"(.+?)"/g;
var m;
while (m = re.exec(text)) {
arr.push({
filename:m[1],
text:store.loadFile(m[1]) // TODO: if missing?
text:store.loadFile(m[1]) || store.loadFile('local/'+m[1]) // TODO??
});
}
}
@ -836,7 +836,7 @@ function toggleDisassembly() {
}
function resetAndDebug() {
if (platform.setupDebug) {
if (platform.setupDebug && platform.readAddress) { // TODO??
clearBreakpoint();
_resume();
platform.reset();

View File

@ -1087,7 +1087,7 @@ function compileVerilator(code, platform, options) {
function compileYosys(code, platform, options) {
loadNative("yosys");
var errors = [];
var match_fn = makeErrorMatcher(errors, /ERROR: (.+?) in line (.+?[.]v):(\d+) (.+)/i, 3, 4);
var match_fn = makeErrorMatcher(errors, /ERROR: (.+?) in line (.+?[.]v):(\d+)[: ]+(.+)/i, 3, 4);
starttime();
var yosys_mod = yosys({
wasmBinary:wasmBlob['yosys'],
@ -1101,7 +1101,13 @@ function compileYosys(code, platform, options) {
FS.writeFile(topmod+".v", code);
writeDependencies(options.dependencies, FS, errors);
starttime();
yosys_mod.callMain(["-q", "-o", topmod+".json", "-S", topmod+".v"]);
try {
yosys_mod.callMain(["-q", "-o", topmod+".json", "-S", topmod+".v"]);
} catch (e) {
console.log(e);
endtime("compile");
return {errors:errors};
}
endtime("compile");
if (errors.length) return {errors:errors};
try {