mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-12-22 12:30:01 +00:00
more Verilog code; inline asm for depends; fixed tank
This commit is contained in:
parent
7e04a15670
commit
b2beb2670c
@ -1,5 +1,17 @@
|
||||
`include "hvsync_generator.v"
|
||||
|
||||
/*
|
||||
Segment bit indices:
|
||||
|
||||
6666
|
||||
1 5
|
||||
1 5
|
||||
0000
|
||||
2 4
|
||||
2 4
|
||||
3333
|
||||
*/
|
||||
|
||||
module seven_segment_decoder(digit, segments);
|
||||
|
||||
input [3:0] digit;
|
||||
|
@ -1,6 +1,7 @@
|
||||
`ifndef CPU16_H
|
||||
`define CPU16_H
|
||||
|
||||
// include ALU module
|
||||
`include "cpu8.v"
|
||||
|
||||
/*
|
||||
@ -17,34 +18,35 @@
|
||||
11+++aaa ######## immediate binary operation
|
||||
*/
|
||||
|
||||
module CPU16(clk, reset, halt, busy,
|
||||
module CPU16(clk, reset, hold, busy,
|
||||
address, data_in, data_out, write);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
input halt;
|
||||
input hold;
|
||||
output busy;
|
||||
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 [15:0] regs[0:7]; // 8 16-bit registers
|
||||
reg [2:0] state; // CPU state
|
||||
|
||||
reg carry;
|
||||
reg zero;
|
||||
reg neg;
|
||||
wire [2:0] flags = { neg, zero, carry };
|
||||
reg carry; // carry flag
|
||||
reg zero; // zero flag
|
||||
reg neg; // negative flag
|
||||
|
||||
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 Bconst = opcode[15]; // TODO
|
||||
wire Bload = opcode[11]; // TODO
|
||||
wire [16:0] Y; // ALU 16-bit + carry output
|
||||
reg [3:0] aluop; // ALU operation
|
||||
|
||||
reg [15:0] opcode; // used to decode ALU inputs
|
||||
wire [2:0] rdest = opcode[10:8]; // ALU A input reg.
|
||||
wire [2:0] rsrc = opcode[2:0]; // ALU B input reg.
|
||||
wire Bconst = opcode[15]; // ALU B = 8-bit constant
|
||||
wire Bload = opcode[11]; // ALU B = data bus
|
||||
|
||||
// CPU states
|
||||
localparam S_RESET = 0;
|
||||
localparam S_SELECT = 1;
|
||||
localparam S_DECODE = 2;
|
||||
@ -77,7 +79,7 @@ module CPU16(clk, reset, halt, busy,
|
||||
// state 1: select opcode address
|
||||
S_SELECT: begin
|
||||
write <= 0;
|
||||
if (halt) begin
|
||||
if (hold) begin
|
||||
busy <= 1;
|
||||
state <= S_SELECT;
|
||||
end else begin
|
||||
@ -89,7 +91,6 @@ module CPU16(clk, reset, halt, busy,
|
||||
end
|
||||
// state 2: read/decode opcode
|
||||
S_DECODE: begin
|
||||
opcode <= data_in; // (only use opcode next cycle)
|
||||
casez (data_in)
|
||||
// 00000aaa0++++bbb operation A+B->A
|
||||
16'b00000???0???????: begin
|
||||
@ -180,6 +181,7 @@ module CPU16(clk, reset, halt, busy,
|
||||
state <= S_RESET; // reset
|
||||
end
|
||||
endcase
|
||||
opcode <= data_in; // (only use opcode next cycle)
|
||||
end
|
||||
// state 3: compute ALU op and flags
|
||||
S_COMPUTE: begin
|
||||
@ -198,6 +200,8 @@ module CPU16(clk, reset, halt, busy,
|
||||
|
||||
endmodule
|
||||
|
||||
`ifdef TOPMOD__test_CPU16_top
|
||||
|
||||
module test_CPU16_top(
|
||||
input clk,
|
||||
input reset,
|
||||
@ -221,7 +225,7 @@ module test_CPU16_top(
|
||||
CPU16 cpu(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.halt(0),
|
||||
.hold(0),
|
||||
.busy(busy),
|
||||
.address(address_bus),
|
||||
.data_in(to_cpu),
|
||||
@ -271,3 +275,5 @@ Loop:
|
||||
endmodule
|
||||
|
||||
`endif
|
||||
|
||||
`endif
|
||||
|
@ -233,6 +233,8 @@ module CPU(clk, reset, address, data_in, data_out, write);
|
||||
|
||||
endmodule
|
||||
|
||||
`ifdef TOPMOD__test_CPU_top
|
||||
|
||||
module test_CPU_top(
|
||||
input clk,
|
||||
input reset,
|
||||
@ -275,29 +277,31 @@ module test_CPU_top(
|
||||
to_cpu = rom[address_bus[6:0]];
|
||||
|
||||
initial begin
|
||||
`ifdef EXT_INLINE_ASM
|
||||
// example code: Fibonacci sequence
|
||||
rom = '{
|
||||
`I_CLEAR_CARRY,
|
||||
`I_ZERO_A,
|
||||
`I_CONST_IMM_B,
|
||||
1,
|
||||
`I_COMPUTE(`DEST_A, `OP_ADC), // addr 4
|
||||
`I_SWAP_AB,
|
||||
`I_BRANCH_IF_CARRY(1'b0),
|
||||
4 + 'h80, // correct for ROM offset
|
||||
`I_STORE_A(4'd0),
|
||||
`I_RESET,
|
||||
// leftover elements
|
||||
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
|
||||
__asm
|
||||
|
||||
.arch femto8
|
||||
.org 128
|
||||
.len 128
|
||||
|
||||
Start:
|
||||
zero A ; A <= 0
|
||||
ldb #1 ; B <= 1
|
||||
Loop:
|
||||
add A,B ; A <= A + B
|
||||
swapab ; swap A,B
|
||||
bcc Loop ; repeat until carry set
|
||||
reset ; end of loop; reset CPU
|
||||
|
||||
__endasm
|
||||
};
|
||||
`endif
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`endif
|
||||
|
||||
`endif
|
||||
|
29
presets/verilog/femto8.json
Normal file
29
presets/verilog/femto8.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name":"femto8",
|
||||
"vars":{
|
||||
"reg":{"bits":2, "toks":["a", "b", "ip", "none"]},
|
||||
"unop":{"bits":3, "toks":["zero","loada","inc","dec","asl","lsr","rol","ror"]},
|
||||
"binop":{"bits":3, "toks":["or","and","xor","mov","add","sub","adc","sbb"]},
|
||||
"const4":{"bits":4},
|
||||
"imm8":{"bits":8}
|
||||
},
|
||||
"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":"mov ~reg,[b]", "bits":["11",0,"1011"]},
|
||||
{"fmt":"zero ~reg", "bits":["00",0,"1011"]},
|
||||
{"fmt":"lda #~imm8", "bits":["01","00","1011",0]},
|
||||
{"fmt":"ldb #~imm8", "bits":["01","01","1011",0]},
|
||||
{"fmt":"jmp ~imm8", "bits":["01","10","1011",0]},
|
||||
{"fmt":"sta ~const4", "bits":["1001",0]},
|
||||
{"fmt":"bcc ~imm8", "bits":["1010","0001",0]},
|
||||
{"fmt":"bcs ~imm8", "bits":["1010","0011",0]},
|
||||
{"fmt":"bz ~imm8", "bits":["1010","1100",0]},
|
||||
{"fmt":"bnz ~imm8", "bits":["1010","0100",0]},
|
||||
{"fmt":"clc", "bits":["10001000"]},
|
||||
{"fmt":"swapab", "bits":["10000001"]},
|
||||
{"fmt":"reset", "bits":["10001111"]}
|
||||
]
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
`undef EXT_INLINE_ASM
|
||||
`include "hvsync_generator.v"
|
||||
`include "cpu8.v"
|
||||
`include "cpu16.v"
|
||||
`define EXT_INLINE_ASM
|
||||
|
||||
// uncomment to see scope view
|
||||
//`define DEBUG
|
||||
|
@ -1,15 +1,18 @@
|
||||
`include "hvsync_generator.v"
|
||||
`include "digits10.v"
|
||||
|
||||
module RAM_1KB(clk, addr, din, dout, we);
|
||||
module RAM(clk, addr, din, dout, we);
|
||||
|
||||
parameter A = 10; // # of address bits
|
||||
parameter D = 8; // # of data bits
|
||||
|
||||
input clk; // clock
|
||||
input [9:0] addr; // 10-bit address
|
||||
input [7:0] din; // 8-bit data input
|
||||
output [7:0] dout; // 8-bit data output
|
||||
input [A-1:0] addr; // 10-bit address
|
||||
input [D-1:0] din; // 8-bit data input
|
||||
output [D-1:0] dout; // 8-bit data output
|
||||
input we; // write enable
|
||||
|
||||
reg [7:0] mem [0:1023]; // 1024x8 bit memory
|
||||
reg [D-1:0] mem [0:(1<<A)-1]; // 1024x8 bit memory
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (we) // if write enabled
|
||||
@ -34,7 +37,8 @@ module test_ram1_top(clk, reset, hsync, vsync, rgb);
|
||||
reg [7:0] ram_write;
|
||||
reg ram_writeenable = 0;
|
||||
|
||||
RAM_1KB ram(
|
||||
// RAM to hold 32x32 array of bytes
|
||||
RAM ram(
|
||||
.clk(clk),
|
||||
.dout(ram_read),
|
||||
.din(ram_write),
|
||||
@ -51,35 +55,45 @@ module test_ram1_top(clk, reset, hsync, vsync, rgb);
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
wire [4:0] row = vpos[7:3]; // 5-bit row, vpos / 8
|
||||
wire [4:0] col = hpos[7:3]; // 5-bit column, hpos / 8
|
||||
wire [2:0] rom_yofs = vpos[2:0]; // scanline of cell
|
||||
wire [4:0] rom_bits; // 5 pixels per scanline
|
||||
|
||||
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] yofs = vpos[2:0];
|
||||
wire [2:0] xofs = hpos[2:0];
|
||||
wire [4:0] bits;
|
||||
|
||||
assign ram_addr = {row,col};
|
||||
wire [3:0] digit = ram_read[3:0]; // read digit from RAM
|
||||
wire [2:0] xofs = hpos[2:0]; // which pixel to draw (0-7)
|
||||
|
||||
assign ram_addr = {row,col}; // 10-bit RAM address
|
||||
|
||||
// digits ROM
|
||||
digits10_case numbers(
|
||||
.digit(digit),
|
||||
.yofs(yofs),
|
||||
.bits(bits)
|
||||
.yofs(rom_yofs),
|
||||
.bits(rom_bits)
|
||||
);
|
||||
|
||||
// extract bit from ROM output
|
||||
wire r = display_on && 0;
|
||||
wire g = display_on && bits[~xofs];
|
||||
wire g = display_on && rom_bits[~xofs];
|
||||
wire b = display_on && 0;
|
||||
assign rgb = {b,g,r};
|
||||
|
||||
// increment the current RAM cell
|
||||
always @(posedge clk)
|
||||
if (display_on && vpos[2:0] == 7)
|
||||
case (hpos[2:0])
|
||||
6: begin
|
||||
ram_write <= (ram_read + 1);
|
||||
ram_writeenable <= 1;
|
||||
end
|
||||
7: ram_writeenable <= 0;
|
||||
endcase
|
||||
case (hpos[2:0])
|
||||
// on 7th pixel of cell
|
||||
6: begin
|
||||
// increment RAM cell
|
||||
ram_write <= (ram_read + 1);
|
||||
// only enable write on last scanline of cell
|
||||
ram_writeenable <= (vpos[2:0] == 7);
|
||||
end
|
||||
// on 8th pixel of cell
|
||||
7: begin
|
||||
// disable write
|
||||
ram_writeenable <= 0;
|
||||
end
|
||||
endcase
|
||||
|
||||
endmodule
|
||||
|
175
presets/verilog/sharedbuffer.v
Normal file
175
presets/verilog/sharedbuffer.v
Normal file
@ -0,0 +1,175 @@
|
||||
`include "hvsync_generator.v"
|
||||
`include "cpu8.v"
|
||||
`include "cpu16.v"
|
||||
|
||||
// uncomment to see scope view
|
||||
//`define DEBUG
|
||||
|
||||
module shared_framebuffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
address_bus, to_cpu, from_cpu, write_enable
|
||||
`ifdef DEBUG
|
||||
, output [15: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.regs[cpu.IP];
|
||||
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;
|
||||
|
||||
reg [15:0] ram[0:32767];
|
||||
reg [15:0] rom[0:1023];
|
||||
|
||||
output wire [15:0] address_bus;
|
||||
output reg [15:0] to_cpu;
|
||||
output wire [15:0] from_cpu;
|
||||
output wire write_enable;
|
||||
|
||||
CPU16 cpu(.clk(clk),
|
||||
.reset(reset),
|
||||
.hold(hold),
|
||||
.busy(busy),
|
||||
.address(address_bus),
|
||||
.data_in(to_cpu),
|
||||
.data_out(from_cpu),
|
||||
.write(write_enable));
|
||||
|
||||
always @(posedge clk)
|
||||
if (write_enable) begin
|
||||
ram[address_bus[14:0]] <= from_cpu;
|
||||
end
|
||||
|
||||
always @(*)
|
||||
if (address_bus[15])
|
||||
to_cpu = rom[address_bus[9:0]];
|
||||
else if (&address_bus[14:8]) begin
|
||||
casez (address_bus[7:0])
|
||||
// special read registers
|
||||
IN_HPOS: to_cpu = {8'b0, hpos[7:0]};
|
||||
IN_VPOS: to_cpu = {8'b0, vpos[7:0]};
|
||||
IN_FLAGS: to_cpu = {11'b0,
|
||||
vsync, hsync, vpaddle, hpaddle, display_on};
|
||||
default: to_cpu = 0;
|
||||
endcase
|
||||
end else
|
||||
to_cpu = ram[address_bus[14:0]];
|
||||
|
||||
hvsync_generator hvsync_gen(
|
||||
.clk(clk),
|
||||
.reset(0),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
// video DMA access
|
||||
reg hold;
|
||||
wire busy;
|
||||
reg [15:0] vline[0:31]; // 32x16 bits = 256 4-color pixels
|
||||
reg [4:0] vindex;
|
||||
reg [15:0] vshift;
|
||||
//wire [15:0] scanread = scanline[hpos[7:3]];
|
||||
//wire [2:0] scanpixel = hpos[2:0];
|
||||
|
||||
always @(posedge clk) begin
|
||||
// has CPU released the bus?
|
||||
if (busy) begin
|
||||
// write from main RAM -> scanline RAM
|
||||
vline[vindex] <= ram[{2'b10,vpos[7:0],vindex}];
|
||||
// end of scanline read?
|
||||
if (&vindex)
|
||||
hold <= 0; // release CPU
|
||||
vindex <= vindex + 1; // next address
|
||||
end else if (hpos >= 256 && hpos < 256+4 && vpos < 240) begin
|
||||
hold <= 1; // start DMA mode, hold CPU
|
||||
end else if (!hpos[8]) begin
|
||||
// load next word from vline buffer
|
||||
if (!&hpos[2:0]) begin
|
||||
vshift <= vline[vindex];
|
||||
vindex <= vindex + 1;
|
||||
end else
|
||||
vshift <= {2'b0, vshift[15:2]};
|
||||
// decode scanline RAM to RGB output
|
||||
rgb <= vshift[3:0];
|
||||
end else
|
||||
rgb <= 0;
|
||||
end
|
||||
|
||||
/*
|
||||
reg [14:0] vpu_read;
|
||||
reg [15:0] vpu_buffer;
|
||||
reg [3:0] rgb;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (!hpos[8] && !vpos[8]) begin
|
||||
if (hpos[1:0] == 0) begin
|
||||
vpu_buffer <= ram[vpu_read];
|
||||
vpu_read <= vpu_read + 1;
|
||||
end
|
||||
//rgb <= ram[{vpos[6:0],hpos[7:0]}][3:0];
|
||||
case (hpos[1:0])
|
||||
0: rgb <= vpu_buffer[3:0];
|
||||
1: rgb <= vpu_buffer[7:4];
|
||||
2: rgb <= vpu_buffer[11:8];
|
||||
3: rgb <= vpu_buffer[15:12];
|
||||
endcase
|
||||
end else begin
|
||||
rgb <= 0;
|
||||
if (vpos[8])
|
||||
vpu_read <= 0;
|
||||
end
|
||||
end
|
||||
*/
|
||||
|
||||
`ifdef EXT_INLINE_ASM
|
||||
initial begin
|
||||
rom = '{
|
||||
__asm
|
||||
.arch femto16
|
||||
.org 32768
|
||||
.len 1024
|
||||
|
||||
.define IN_HPOS $7f00
|
||||
.define IN_VPOS $7f01
|
||||
.define IN_FLAGS $7f02
|
||||
|
||||
.define F_DISPLAY 1
|
||||
.define F_HPADDLE 2
|
||||
.define F_VPADDLE 4
|
||||
.define F_HSYNC 8
|
||||
.define F_VSYNC 16
|
||||
|
||||
Start:
|
||||
mov ax,#0
|
||||
mov bx,ax
|
||||
Loop:
|
||||
mov [bx],ax
|
||||
inc bx
|
||||
inc ax
|
||||
bnz Loop
|
||||
reset
|
||||
__endasm
|
||||
};
|
||||
end
|
||||
`endif
|
||||
|
||||
endmodule
|
104
presets/verilog/sound_generator.v
Normal file
104
presets/verilog/sound_generator.v
Normal file
@ -0,0 +1,104 @@
|
||||
`include "hvsync_generator.v"
|
||||
`include "lfsr.v"
|
||||
|
||||
module sound_generator(clk, reset, spkr,
|
||||
lfo_freq,noise_freq, vco_freq,
|
||||
vco_select, noise_select, lfo_shift, mixer);
|
||||
|
||||
input clk, reset;
|
||||
output reg spkr = 0; // module output
|
||||
|
||||
input [9:0] lfo_freq; // LFO frequency (10 bits)
|
||||
input [11:0] noise_freq; // noise frequency (12 bits)
|
||||
input [11:0] vco_freq; // VCO frequency (12 bits)
|
||||
input vco_select; // 1 = LFO modulates VCO
|
||||
input noise_select; // 1 = LFO modulates Noise
|
||||
input [2:0] lfo_shift; // LFO modulation depth
|
||||
input [2:0] mixer; // mix enable {LFO, Noise, VCO}
|
||||
|
||||
reg [3:0] div16; // divide-by-16 counter
|
||||
reg [17:0] lfo_count; // LFO counter (18 bits)
|
||||
reg lfo_state; // LFO output
|
||||
reg [12:0] noise_count; // Noise counter (13 bits)
|
||||
reg noise_state; // Noise output
|
||||
reg [12:0] vco_count; // VCO counter (12 bits)
|
||||
reg vco_state; // VCO output
|
||||
|
||||
reg [15:0] lfsr; // LFSR output
|
||||
|
||||
LFSR #(16,16'b1000000001011,0) lfsr_gen(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.enable(div16 == 0 && noise_count == 0),
|
||||
.lfsr(lfsr)
|
||||
);
|
||||
|
||||
// create triangle waveform from LFO
|
||||
wire [11:0] lfo_triangle = lfo_count[17] ? ~lfo_count[17:6] : lfo_count[17:6];
|
||||
wire [11:0] vco_delta = lfo_triangle >> lfo_shift;
|
||||
|
||||
always @(posedge clk) begin
|
||||
// divide clock by 64
|
||||
div16 <= div16 + 1;
|
||||
if (div16 == 0) begin
|
||||
// VCO oscillator
|
||||
if (reset || vco_count == 0) begin
|
||||
vco_state <= ~vco_state;
|
||||
if (vco_select)
|
||||
vco_count <= vco_freq + vco_delta;
|
||||
else
|
||||
vco_count <= vco_freq + 0;
|
||||
end else
|
||||
vco_count <= vco_count - 1;
|
||||
// LFO oscillator
|
||||
if (reset || lfo_count == 0) begin
|
||||
lfo_state <= ~lfo_state;
|
||||
lfo_count <= {lfo_freq, 8'b0};
|
||||
end else
|
||||
lfo_count <= lfo_count - 1;
|
||||
// Noise oscillator
|
||||
if (reset || noise_count == 0) begin
|
||||
if (lfsr[0])
|
||||
noise_state <= ~noise_state;
|
||||
if (noise_select)
|
||||
noise_count <= noise_freq + vco_delta;
|
||||
else
|
||||
noise_count <= noise_freq + 0;
|
||||
end else
|
||||
noise_count <= noise_count - 1;
|
||||
// Mixer
|
||||
spkr <= (lfo_state | ~mixer[2])
|
||||
& (noise_state | ~mixer[1])
|
||||
& (vco_state | ~mixer[0]);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module test_snchip_top(clk, reset, hsync, vsync, rgb, spkr);
|
||||
|
||||
input clk, reset;
|
||||
output hsync;
|
||||
output vsync;
|
||||
output spkr;
|
||||
output [2:0] rgb;
|
||||
|
||||
// don't output a valid sync signal
|
||||
assign hsync = 0;
|
||||
assign vsync = 0;
|
||||
assign rgb = {spkr,1'b0,1'b0};
|
||||
|
||||
sound_generator sndgen(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.spkr(spkr),
|
||||
.lfo_freq(1000),
|
||||
.noise_freq(90),
|
||||
.vco_freq(250),
|
||||
.vco_select(1),
|
||||
.noise_select(1),
|
||||
.lfo_shift(1),
|
||||
.mixer(3)
|
||||
);
|
||||
|
||||
endmodule
|
@ -295,10 +295,10 @@ module tank_controller(clk, reset, hpos, vpos, hsync, vsync,
|
||||
reg collision_detected;
|
||||
|
||||
always @(posedge clk)
|
||||
if (collision_gfx)
|
||||
collision_detected <= collision_gfx;
|
||||
else if (vsync)
|
||||
if (vstart)
|
||||
collision_detected <= 0;
|
||||
else if (collision_gfx)
|
||||
collision_detected <= 1;
|
||||
|
||||
// sine lookup (4 bits input, 4 signed bits output)
|
||||
|
||||
@ -325,7 +325,7 @@ module tank_controller(clk, reset, hpos, vpos, hsync, vsync,
|
||||
player_y_fixed <= initial_y << 4;
|
||||
end else begin
|
||||
// movement
|
||||
if (collision_detected && vpos[1]) begin
|
||||
if (collision_detected && vpos[3:1] == 0) begin
|
||||
if (vpos[0])
|
||||
player_x_fixed <= player_x_fixed + 12'(sin_16x4(player_rot+8));
|
||||
else
|
||||
|
206
presets/verilog/sprite_scanline_renderer.v
Normal file
206
presets/verilog/sprite_scanline_renderer.v
Normal file
@ -0,0 +1,206 @@
|
||||
`include "hvsync_generator.v"
|
||||
|
||||
module example_bitmap_rom(addr, data);
|
||||
|
||||
input [15:0] addr;
|
||||
output [15:0] data;
|
||||
|
||||
reg [15:0] bitarray[0:255];
|
||||
|
||||
assign data = bitarray[addr & 15];
|
||||
|
||||
initial begin/*{w:16,h:16,bpw:16,count:1}*/
|
||||
bitarray[8'h00] = 16'b11110000000;
|
||||
bitarray[8'h01] = 16'b100001000000;
|
||||
bitarray[8'h02] = 16'b1111111100000;
|
||||
bitarray[8'h03] = 16'b1111111100000;
|
||||
bitarray[8'h04] = 16'b11110000000;
|
||||
bitarray[8'h05] = 16'b11111111110000;
|
||||
bitarray[8'h06] = 16'b111100001111000;
|
||||
bitarray[8'h07] = 16'b1111101101111100;
|
||||
bitarray[8'h08] = 16'b1101100001101111;
|
||||
bitarray[8'h09] = 16'b1101111111100110;
|
||||
bitarray[8'h0a] = 16'b1001111111100000;
|
||||
bitarray[8'h0b] = 16'b1111111100000;
|
||||
bitarray[8'h0c] = 16'b1110011100000;
|
||||
bitarray[8'h0d] = 16'b1100001100000;
|
||||
bitarray[8'h0e] = 16'b1100001100000;
|
||||
bitarray[8'h0f] = 16'b11100001110000;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
|
||||
rom_addr, rom_data);
|
||||
|
||||
parameter NB = 5;
|
||||
parameter MB = 2;
|
||||
|
||||
localparam N = 1<<NB;
|
||||
localparam M = 1<<MB;
|
||||
|
||||
input clk, reset;
|
||||
input [8:0] hpos;
|
||||
input [8:0] vpos;
|
||||
output [3:0] rgb;
|
||||
|
||||
output [15:0] rom_addr;
|
||||
input [15:0] rom_data;
|
||||
|
||||
reg [7:0] sprite_xpos[0:N-1];
|
||||
reg [7:0] sprite_ypos[0:N-1];
|
||||
reg [7:0] sprite_attr[0:N-1];
|
||||
|
||||
reg [NB-1:0] sprite_to_line[0:M-1];
|
||||
reg [7:0] line_xpos[0:M-1];
|
||||
reg [7:0] line_yofs[0:M-1];
|
||||
reg [7:0] line_attr[0:M-1];
|
||||
reg line_active[0:M-1];
|
||||
reg [3:0] scanline[0:511];
|
||||
reg [NB-1:0] i; // 0..N-1
|
||||
reg [MB-1:0] j; // 0..M-1
|
||||
reg [MB-1:0] k; // 0..M-1
|
||||
reg [7:0] z;
|
||||
reg [8:0] write_ofs;
|
||||
|
||||
wire [8:0] read_bufidx = {vpos[0], hpos[7:0]};
|
||||
reg [15:0] out_bitmap;
|
||||
reg [7:0] out_attr;
|
||||
wire [NB-1:0] move_index = hpos[NB-1:0];
|
||||
|
||||
/*
|
||||
0: read sprite_ypos[i]
|
||||
1: check ypos, write line_ypos[j]
|
||||
...
|
||||
0: read line_xpos[0]
|
||||
1: store xpos
|
||||
2: read line_ypos[0]
|
||||
3: store ypos
|
||||
4: read line_attr[0]
|
||||
5: store attr
|
||||
8: write 16 pixels
|
||||
*/
|
||||
|
||||
always @(posedge clk) begin
|
||||
|
||||
// reset every frame, don't draw vpos >= 256
|
||||
if (reset || vpos[8]) begin
|
||||
// initialize counters, even though it works w/o it
|
||||
i <= 0;
|
||||
j <= 0;
|
||||
k <= 0;
|
||||
// wiggle sprites randomly once per frame
|
||||
if (vpos == 256 && hpos < N) begin
|
||||
sprite_xpos[move_index] <= sprite_xpos[move_index] + 8'(($random&3)-1);
|
||||
sprite_ypos[move_index] <= sprite_ypos[move_index] + 8'(($random&3)-1);
|
||||
end
|
||||
end else
|
||||
if (hpos < N*2) begin
|
||||
// select the sprites that will appear in this scanline
|
||||
case (hpos[0])
|
||||
// compute Y offset of sprite relative to scanline
|
||||
0: z <= 8'(vpos - sprite_ypos[i]);
|
||||
// sprite is active if Y offset is 0..15
|
||||
1: begin
|
||||
if (z < 16) begin
|
||||
line_yofs[j] <= z; // save Y offset
|
||||
sprite_to_line[j] <= i; // save main array index
|
||||
line_active[j] <= 1; // mark sprite active
|
||||
j <= j + 1; // inc counter
|
||||
end
|
||||
i <= i + 1; // inc main array counter
|
||||
end
|
||||
endcase
|
||||
end else if (hpos < N*2+M*4) begin
|
||||
// copy sprites from main array to local array
|
||||
case (hpos[1:0])
|
||||
0: i <= sprite_to_line[j];
|
||||
// transfer sprite X pos to line array
|
||||
1: line_xpos[j] <= sprite_xpos[i];
|
||||
// transfer sprite attribte to line array
|
||||
2: line_attr[j] <= sprite_attr[i];
|
||||
// increment 2nd array counter
|
||||
3: j <= j + 1;
|
||||
endcase
|
||||
end else if (hpos < N*2+M*4+M*32) begin
|
||||
i <= 0;
|
||||
j <= 0;
|
||||
if (hpos[4:0] < 16) begin
|
||||
// render sprites into write buffer
|
||||
case (hpos[4:0])
|
||||
// load scanline buffer offset to write
|
||||
0: write_ofs <= {~vpos[0], line_xpos[k]};
|
||||
// set ROM address and fetch bitmap
|
||||
1: rom_addr <= {4'b0, line_attr[k][7:4], line_yofs[k]};
|
||||
// fetch 0 if sprite is inactive
|
||||
2: out_bitmap <= line_active[k] ? rom_data : 0;
|
||||
// load attribute for sprite
|
||||
3: out_attr <= line_attr[k];
|
||||
// disable sprite for next scanline
|
||||
6: line_active[k] <= 0;
|
||||
// go to next sprite in 2ndary buffer
|
||||
7: k <= k + 1;
|
||||
endcase
|
||||
end else begin
|
||||
// write color to scanline buffer if low bit == 1
|
||||
if (out_bitmap[0])
|
||||
scanline[write_ofs] <= out_attr[3:0];
|
||||
// shift bits right
|
||||
out_bitmap <= out_bitmap >> 1;
|
||||
// increment to next scanline pixel
|
||||
write_ofs <= write_ofs + 1;
|
||||
end
|
||||
end
|
||||
|
||||
// read and clear buffer
|
||||
rgb <= scanline[read_bufidx];
|
||||
scanline[read_bufidx] <= 0;
|
||||
end
|
||||
|
||||
initial
|
||||
begin
|
||||
sprite_xpos[0] = 0;
|
||||
sprite_ypos[0] = 0;
|
||||
sprite_attr[0] = 1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module test_scanline_render_top(clk, reset, hsync, vsync, rgb);
|
||||
|
||||
input clk, reset;
|
||||
output hsync, vsync;
|
||||
output [3: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 [15:0] rom_addr;
|
||||
wire [15:0] rom_data;
|
||||
|
||||
example_bitmap_rom bitmap_rom(
|
||||
.addr(rom_addr),
|
||||
.data(rom_data)
|
||||
);
|
||||
|
||||
sprite_scanline_renderer ssr(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos),
|
||||
.rgb(rgb),
|
||||
.rom_addr(rom_addr),
|
||||
.rom_data(rom_data)
|
||||
);
|
||||
|
||||
endmodule
|
@ -1,4 +1,5 @@
|
||||
`include "hvsync_generator.v"
|
||||
`include "digits10.v"
|
||||
`include "sprite_rotation.v"
|
||||
|
||||
module minefield(hpos, vpos, mine_gfx);
|
||||
@ -100,7 +101,7 @@ module playfield(hpos, vpos, playfield_gfx);
|
||||
|
||||
endmodule
|
||||
|
||||
module mine_test_top(clk, reset, hsync, vsync, rgb, switches_p1, switches_p2);
|
||||
module tank_game_top(clk, reset, hsync, vsync, rgb, switches_p1, switches_p2);
|
||||
|
||||
input clk, reset;
|
||||
input [7:0] switches_p1;
|
||||
@ -114,7 +115,7 @@ module mine_test_top(clk, reset, hsync, vsync, rgb, switches_p1, switches_p2);
|
||||
wire mine_gfx;
|
||||
wire playfield_gfx;
|
||||
wire tank1_gfx, tank2_gfx;
|
||||
|
||||
|
||||
hvsync_generator hvsync_gen(
|
||||
.clk(clk),
|
||||
.reset(0),
|
||||
|
@ -228,7 +228,12 @@ var AnimationTimer = function(frequencyHz, callback) {
|
||||
}
|
||||
if (!useReqAnimFrame || ts - lastts > intervalMsec/2) {
|
||||
if (running) {
|
||||
callback();
|
||||
try {
|
||||
callback();
|
||||
} catch (e) {
|
||||
running = false;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
if (ts - lastts < intervalMsec*30) {
|
||||
lastts += intervalMsec;
|
||||
|
@ -4,23 +4,24 @@ var VERILOG_PRESETS = [
|
||||
{id:'clock_divider.v', name:'Clock Divider'},
|
||||
{id:'hvsync_generator.v', name:'Video Sync Generator'},
|
||||
{id:'test_hvsync.v', name:'Test Pattern'},
|
||||
{id:'digits10.v', name:'Bitmapped Digits'},
|
||||
{id:'7segment.v', name:'7-Segment Decoder'},
|
||||
{id:'digits10.v', name:'Bitmapped Digits'},
|
||||
{id:'scoreboard.v', name:'Scoreboard'},
|
||||
{id:'ball_slip_counter.v', name:'Ball Motion (slipping counter)'},
|
||||
{id:'ball_paddle.v', name:'Brick Smash Game'},
|
||||
{id:'ram1.v', name:'RAM Text Display'},
|
||||
{id:'sprite_bitmap.v', name:'Sprite Bitmaps'},
|
||||
{id:'sprite_renderer.v', name:'Sprite Rendering'},
|
||||
{id:'sprite_multiple.v', name:'Multiple Sprites'},
|
||||
{id:'sprite_rotation.v', name:'Sprite Rotation'},
|
||||
{id:'tank.v', name:'Tank Game'},
|
||||
{id:'cpu8.v', name:'Simple 8-Bit CPU'},
|
||||
{id:'racing_game_cpu.v', name:'Racing Game With CPU'},
|
||||
{id:'music.v', name:'3-Voice Music'},
|
||||
{id:'sound_generator.v', name:'Sound Generator'},
|
||||
{id:'lfsr.v', name:'Linear Feedback Shift Register'},
|
||||
{id:'starfield.v', name:'Scrolling Starfield'},
|
||||
{id:'racing_game.v', name:'Racing Game'},
|
||||
{id:'cpu8.v', name:'Simple 8-Bit CPU'},
|
||||
{id:'racing_game_cpu.v', name:'Racing Game with CPU'},
|
||||
{id:'framebuffer.v', name:'Frame Buffer'},
|
||||
{id:'sprite_scanline_renderer.v', name:'Sprite Scanline Renderer'},
|
||||
];
|
||||
|
||||
var VERILOG_KEYCODE_MAP = makeKeycodeMap([
|
||||
@ -80,6 +81,9 @@ var vl_stopped = false;
|
||||
var VL_MODDIV_III = this.VL_MODDIV_III = function(lbits,lhs,rhs) {
|
||||
return (((rhs)==0)?0:(lhs)%(rhs)); }
|
||||
|
||||
var VL_MODDIVS_III = this.VL_MODDIVS_III = function(lbits,lhs,rhs) {
|
||||
return (((rhs)==0)?0:(lhs)%(rhs)); }
|
||||
|
||||
var VL_REDXOR_32 = this.VL_REDXOR_32 = function(r) {
|
||||
r=(r^(r>>1)); r=(r^(r>>2)); r=(r^(r>>4)); r=(r^(r>>8)); r=(r^(r>>16));
|
||||
return r;
|
||||
@ -98,6 +102,8 @@ var vl_stopped = false;
|
||||
|
||||
var VL_RAND_RESET_I = this.VL_RAND_RESET_I = function(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
|
||||
|
||||
var VL_RANDOM_I = this.VL_RANDOM_I = function(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
|
||||
|
||||
//
|
||||
|
||||
function VerilatorBase() {
|
||||
@ -182,7 +188,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
var useAudio = false;
|
||||
var videoWidth = 256+20;
|
||||
var videoHeight = 240+20;
|
||||
var maxVideoBlankLines = 40; // vertical hold
|
||||
var maxVideoLines = 262+40; // vertical hold
|
||||
var idata, timer;
|
||||
var gen;
|
||||
var frameRate = 60;
|
||||
@ -218,6 +224,14 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
0xffff22ff,
|
||||
0xffffff22,
|
||||
0xffffffff,
|
||||
0xff999999,
|
||||
0xff9999ff,
|
||||
0xff99ff99,
|
||||
0xff99ffff,
|
||||
0xffff9999,
|
||||
0xffff99ff,
|
||||
0xffffff99,
|
||||
0xff666666,
|
||||
];
|
||||
|
||||
var debugCond;
|
||||
@ -233,8 +247,8 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
function updateInspectionFrame() {
|
||||
useAudio = false;
|
||||
if (inspect_obj && inspect_sym) {
|
||||
var COLOR_BIT_OFF = 0xffff3333;
|
||||
var COLOR_BIT_ON = 0xffffffff;
|
||||
var COLOR_BIT_OFF = 0xffff6666;
|
||||
var COLOR_BIT_ON = 0xffff9999;
|
||||
/*
|
||||
for (var y=0; y<videoHeight; y++) {
|
||||
var val = inspect_data[y * videoWidth];
|
||||
@ -275,6 +289,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
gen.switches_p1 = switches[0];
|
||||
gen.switches_p2 = switches[1];
|
||||
gen.switches_gen = switches[2];
|
||||
var totalz = 0;
|
||||
for (var y=0; y<videoHeight; y++) {
|
||||
gen.hpaddle = y > paddle_x ? 1 : 0;
|
||||
gen.vpaddle = y > paddle_y ? 1 : 0;
|
||||
@ -283,15 +298,16 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
if (trace) {
|
||||
inspect_data[i] = inspect_obj[inspect_sym];
|
||||
}
|
||||
idata[i++] = RGBLOOKUP[gen.rgb & 7];
|
||||
idata[i++] = RGBLOOKUP[gen.rgb & 15];
|
||||
}
|
||||
var z=0;
|
||||
while (!gen.hsync && z++<videoWidth) vidtick();
|
||||
while (gen.hsync && z++<videoWidth) vidtick();
|
||||
totalz += z;
|
||||
}
|
||||
var z=0;
|
||||
while (!gen.vsync && z++<videoWidth*maxVideoBlankLines) vidtick();
|
||||
while (gen.vsync && z++<videoWidth*maxVideoBlankLines) vidtick();
|
||||
var maxz = videoWidth*maxVideoLines;
|
||||
while (!gen.vsync && totalz++ < maxz) vidtick();
|
||||
while (gen.vsync && totalz++ < maxz) vidtick();
|
||||
updateInspectionFrame();
|
||||
video.updateFrame();
|
||||
updateInspectionPostFrame();
|
||||
|
@ -207,7 +207,7 @@ var Assembler = function(spec) {
|
||||
if (line == '')
|
||||
return; // empty line
|
||||
// look at each rule in order
|
||||
if (!spec) { fatal("Need to load .spec first"); return; }
|
||||
if (!spec) { fatal("Need to load .arch first"); return; }
|
||||
var lastError;
|
||||
for (var i=0; i<spec.rules.length; i++) {
|
||||
var rule = spec.rules[i];
|
||||
@ -277,3 +277,20 @@ var Assembler = function(spec) {
|
||||
output:outwords, asmlines:asmlines, errors:errors, fixups:fixups};
|
||||
}
|
||||
}
|
||||
|
||||
// Main
|
||||
if (typeof module !== 'undefined' && require.main === module) {
|
||||
var fs = require('fs');
|
||||
var stdinBuffer = fs.readFileSync(0);
|
||||
var code = stdinBuffer.toString();
|
||||
var asm = new Assembler();
|
||||
asm.loadFile = function(filename) {
|
||||
return fs.readFileSync(filename, 'utf8');
|
||||
};
|
||||
var out = asm.assembleFile(code);
|
||||
if (out.errors) {
|
||||
console.log(out.errors);
|
||||
} else {
|
||||
console.log(out.outwords);
|
||||
}
|
||||
}
|
||||
|
@ -1043,12 +1043,13 @@ function detectTopModuleName(code) {
|
||||
return topmod;
|
||||
}
|
||||
|
||||
function writeDependencies(depends, FS, errors) {
|
||||
function writeDependencies(depends, FS, errors, callback) {
|
||||
if (depends) {
|
||||
for (var i=0; i<depends.length; i++) {
|
||||
var d = depends[i];
|
||||
var text;
|
||||
if (d.text) {
|
||||
FS.writeFile(d.filename, d.text, {encoding:'utf8'});
|
||||
text = d.text;
|
||||
} else {
|
||||
// load from network (hopefully cached)
|
||||
// TODO: get from indexeddb?
|
||||
@ -1057,11 +1058,15 @@ function writeDependencies(depends, FS, errors) {
|
||||
xhr.open("GET", path, false); // synchronous request
|
||||
xhr.send(null);
|
||||
if (xhr.response) {
|
||||
FS.writeFile(d.filename, xhr.response, {encoding:'utf8'});
|
||||
text = xhr.response;
|
||||
} else {
|
||||
console.log("Could not load " + path);
|
||||
}
|
||||
}
|
||||
if (callback)
|
||||
text = callback(d, text);
|
||||
if (text)
|
||||
FS.writeFile(d.filename, text, {encoding:'utf8'});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1129,20 +1134,15 @@ function compileASM(asmcode, platform, options) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function compileVerilator(code, platform, options) {
|
||||
loadNative("verilator_bin");
|
||||
load("verilator2js");
|
||||
var errors = [];
|
||||
var asmlines = [];
|
||||
// compile inline asm
|
||||
// TODO: keep line numbers
|
||||
function compileInlineASM(code, platform, options, errors, asmlines) {
|
||||
code = code.replace(/__asm\b([\s\S]+?)\b__endasm\b/g, function(s,asmcode,index) {
|
||||
var firstline = code.substr(0,index).match(/\n/g).length;
|
||||
var asmout = compileASM(asmcode, platform, options);
|
||||
if (asmout.errors && asmout.errors.length) {
|
||||
errors = asmout.errors;
|
||||
for (var i=0; i<errors.length; i++)
|
||||
errors[i].line += firstline;
|
||||
for (var i=0; i<asmout.errors.length; i++) {
|
||||
asmout.errors[i].line += firstline;
|
||||
errors.push(asmout.errors[i]);
|
||||
}
|
||||
return "";
|
||||
} else if (asmout.output) {
|
||||
var s = "";
|
||||
@ -1157,6 +1157,15 @@ function compileVerilator(code, platform, options) {
|
||||
return s;
|
||||
}
|
||||
});
|
||||
return code;
|
||||
}
|
||||
|
||||
function compileVerilator(code, platform, options) {
|
||||
loadNative("verilator_bin");
|
||||
load("verilator2js");
|
||||
var errors = [];
|
||||
var asmlines = [];
|
||||
code = compileInlineASM(code, platform, options, errors, asmlines);
|
||||
var match_fn = makeErrorMatcher(errors, /%(.+?): (.+?:)?(\d+)?[:]?\s*(.+)/i, 3, 4);
|
||||
var verilator_mod = verilator_bin({
|
||||
wasmBinary:wasmBlob['verilator_bin'],
|
||||
@ -1167,9 +1176,11 @@ function compileVerilator(code, platform, options) {
|
||||
var topmod = detectTopModuleName(code);
|
||||
var FS = verilator_mod['FS'];
|
||||
FS.writeFile(topmod+".v", code);
|
||||
writeDependencies(options.dependencies, FS, errors);
|
||||
writeDependencies(options.dependencies, FS, errors, function(d, code) {
|
||||
return compileInlineASM(code, platform, options, errors, asmlines);
|
||||
});
|
||||
starttime();
|
||||
verilator_mod.callMain(["--cc", "-O3", "-DEXT_INLINE_ASM",
|
||||
verilator_mod.callMain(["--cc", "-O3", "-DEXT_INLINE_ASM", "-DTOPMOD__"+topmod,
|
||||
"-Wall", "-Wno-DECLFILENAME", "-Wno-UNUSED", '--report-unoptflat',
|
||||
"--x-assign", "fast", "--noassert", "--pins-bv", "33",
|
||||
"--top-module", topmod, topmod+".v"]);
|
||||
|
Loading…
Reference in New Issue
Block a user