preset updates; shadow text for scope view

This commit is contained in:
Steven Hugg 2018-02-09 16:23:14 -06:00
parent 661bbb0ced
commit 9c25aed9fa
5 changed files with 333 additions and 188 deletions

View File

@ -6,7 +6,7 @@
`define OP_XOR 4'h4
`define OP_INC 4'h5
`define OP_DEC 4'h6
`define OP_NOP 4'h7
`define OP_ZERO 4'h7
// operations that generate carry
`define OP_ADD 4'h8
`define OP_SUB 4'h9
@ -24,35 +24,57 @@ module ALU(
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 + 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};
`OP_INC: Y = A + 1;
`OP_DEC: Y = A - 1;
`OP_ZERO: Y = 0;
`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 (@ 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, 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_TO_B { 8'b10000000 }
`define I_CONST_SHORT_A(addr) { 4'b01010, 4'(addr) }
`define I_CONST_SHORT_B(addr) { 4'b01011, 4'(addr) }
`define I_BRANCH_IF_CARRY(carry) { 6'b100100, 1'(carry), 1'b1 }
`define I_RESET { 8'b10000001 }
`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 }
// convenience macros
`define I_ZERO_A `I_COMPUTE(`DEST_A, `OP_ZERO)
`define I_ZERO_B `I_COMPUTE(`DEST_B, `OP_ZERO)
module CPU(
input clk,
@ -75,7 +97,7 @@ module CPU(
reg [7:0] opcode;
wire [3:0] aluop = opcode[3:0];
wire [1:0] opdest = opcode[5:4];
wire memalu = opcode[6];
wire B_or_data = opcode[6];
localparam S_RESET = 0;
localparam S_SELECT = 1;
@ -83,7 +105,7 @@ module CPU(
localparam S_COMPUTE = 3;
localparam S_READ_IP = 4;
ALU alu(.A(A), .B(memalu?data_in:B), .Y(Y), .aluop(aluop));
ALU alu(.A(A), .B(B_or_data?data_in:B), .Y(Y), .aluop(aluop));
always @(posedge clk)
if (reset) begin
@ -118,20 +140,26 @@ module CPU(
IP <= IP + 1;
state <= S_COMPUTE;
end
// ALU A + [B] -> dest
// ALU A + read [B] -> dest
8'b11??????: begin
address <= B;
state <= S_COMPUTE;
end
// A -> write [B]
8'b10000000: begin
address <= B;
// 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'b1001????: begin
8'b1010????: begin
if (
(data_in[0] && (data_in[1] == carry)) ||
(data_in[2] && (data_in[3] == zero)))
@ -143,16 +171,6 @@ module CPU(
end
IP <= IP + 1; // skip immediate
end
// aluop -> A
8'b1010????: begin
A <= {4'b0, data_in[3:0]};
state <= S_SELECT;
end
// aluop -> B
8'b1011????: begin
B <= {4'b0, data_in[3:0]};
state <= S_SELECT;
end
// fall-through RESET
default: begin
state <= S_RESET; // reset
@ -197,8 +215,8 @@ module test_CPU_top(
output [7:0] B
);
reg [7:0] ram[127:0];
reg [7:0] rom[127:0];
reg [7:0] ram[0:127];
reg [7:0] rom[0:127];
assign IP = cpu.IP;
assign A = cpu.A;
@ -221,17 +239,18 @@ module test_CPU_top(
to_cpu = rom[address_bus[6:0]];
initial begin
// ROM starts at address 0x80
rom['h00] = `I_CONST_IMM_A;
rom['h01] = 1;
rom['h02] = `I_CONST_SHORT_B(0);
rom['h03] = `I_COMPUTE(`DEST_A, `OP_ADD);
rom['h04] = `I_COMPUTE(`DEST_B, `OP_ADD);
rom['h05] = `I_STORE_A_TO_B;
//rom['h06] = `I_JUMP_IMM;
rom['h06] = `I_BRANCH_IF_CARRY(0);
rom['h07] = 3 + 'h80; // correct for ROM offset
rom['h08] = `I_RESET;
rom = '{
`I_ZERO_A,
`I_CONST_IMM_B,
1,
`I_COMPUTE(`DEST_A, `OP_ADD), // addr 4
`I_SWAP_AB,
`I_BRANCH_IF_CARRY(0),
3 + 'h80, // correct for ROM offset
`I_RESET,
// leftover elements
120{0}
};
end
endmodule

View File

@ -1,15 +1,17 @@
`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
`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,
@ -22,27 +24,51 @@ module ALU(
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};
`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
`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
/*
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,
@ -53,9 +79,7 @@ module CPU(
output write
);
reg [7:0] ip;
reg [7:0] opcode;
reg [3:0] aluop;
reg [7:0] IP;
reg [7:0] A, B;
reg [8:0] Y;
reg [2:0] state;
@ -63,17 +87,19 @@ module CPU(
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_LOAD_ADDR = 3;
localparam S_STORE_ADDR = 4;
localparam S_COMPUTE = 5;
localparam S_COMPUTE = 3;
localparam S_READ_IP = 4;
wire load_const = opcode[3:1] == 3'b111;
ALU alu(.A(A), .B(B), .Y(Y), .aluop(aluop));
ALU alu(.A(A), .B(memalu?data_in:B), .Y(Y), .aluop(aluop));
always @(posedge clk)
if (reset) begin
@ -83,75 +109,87 @@ module CPU(
case (state)
// state 0: reset
S_RESET: begin
ip <= 8'h80;
IP <= 8'h80;
write <= 0;
state <= S_SELECT;
end
// state 1: select opcode address
S_SELECT: begin
address <= ip;
ip <= ip + 1;
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)
8'b00??????: begin // load constant
if (data_in[5])
B <= {3'b0, data_in[4:0]};
else
A <= {3'b0, data_in[4:0]};
state <= S_SELECT;
end
8'b010?????: begin // read memory
address <= {4'b0, data_in[3:0]};
state <= S_LOAD_ADDR;
end
8'b011?????: begin // write memory
address <= {4'b0, data_in[3:0]};
state <= S_STORE_ADDR;
end
8'b100?????: begin // compute w/ ALU
aluop <= data_in[3:0];
// ALU A + B -> dest
8'b00??????: begin
state <= S_COMPUTE;
end
8'b101000??: begin // branch if flag set/clear
if ((data_in[0] ? carry : zero) ^ data_in[1])
ip <= A;
// 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
opcode <= data_in;
end
// state 3: load address
S_LOAD_ADDR: begin
if (opcode[4])
B <= data_in;
else
A <= data_in;
state <= S_SELECT;
end
// state 4: store address
S_STORE_ADDR: begin
if (opcode[4])
data_out <= B;
else
data_out <= A;
write <= 1;
state <= S_SELECT;
end
// state 5: compute ALU op and flags
// state 3: compute ALU op and flags
S_COMPUTE: begin
if (opcode[4])
B <= Y[7:0];
else
A <= Y[7:0];
carry <= Y[8];
// 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
@ -165,11 +203,18 @@ module test_CPU_top(
output [7:0] address_bus,
output reg [7:0] to_cpu,
output [7:0] from_cpu,
output write_enable
output write_enable,
output [7:0] IP,
output [7:0] A,
output [7:0] B
);
reg [7:0] ram[127:0];
reg [7:0] rom[127:0];
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),
@ -188,16 +233,19 @@ module test_CPU_top(
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;
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

@ -10,80 +10,99 @@ module car_bitmap(yofs, bits);
assign bits = bitarray[yofs];
initial begin/*{w:8,h:16}*/
bitarray[0] = 8'b110000;
bitarray[1] = 8'b1110111;
bitarray[2] = 8'b11100110;
bitarray[3] = 8'b11111111;
bitarray[4] = 8'b11100110;
bitarray[5] = 8'b1100111;
bitarray[6] = 8'b110000;
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'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'b1100111;
bitarray[11] = 8'b11100110;
bitarray[12] = 8'b11111111;
bitarray[13] = 8'b11100110;
bitarray[14] = 8'b1110111;
bitarray[15] = 8'b110000;
end
endmodule
module sprite_renderer(clk, vstart, load, hstart, rom_yofs, rom_bits,
module sprite_renderer(clk, vstart, load, hstart, rom_addr, rom_bits,
gfx, in_progress);
input clk, vstart, load, hstart;
output [3:0] rom_yofs;
output [3:0] rom_addr;
input [7:0] rom_bits;
output gfx;
output in_progress = yactive;
output in_progress = state != WAIT_FOR_VSTART;
reg [2:0] state;
reg [3:0] ycount;
reg [3:0] xcount;
reg yactive;
reg xactive;
reg loading;
reg [7:0] outbits;
localparam WAIT_FOR_VSTART = 0;
localparam WAIT_FOR_LOAD = 1;
localparam LOAD1_SETUP = 2;
localparam LOAD1_FETCH = 3;
localparam WAIT_FOR_HSTART = 4;
localparam DRAW = 5;
always @(posedge clk)
begin
// set a default value (blank) for pixel output
// note: multiple non-blocking assignments are vendor-specific
gfx <= 0;
// load next line? set ROM address
if (yactive && load) begin
rom_yofs <= ~ycount;
loading <= 1;
// ROM address was set, now latch bits from bus
end else if (loading) begin
outbits <= rom_bits;
loading <= 0;
ycount <= ycount + 1;
// start sprite at this vertical scanline
end else if (vstart) begin
yactive <= 1;
//ycount <= 0;
// start sprite at this horizontal clock
end else if (hstart && yactive) begin
xactive <= 1;
//xcount <= 0;
// both X & Y active, set pixel output
end else if (xactive && yactive)
begin
case (state)
WAIT_FOR_VSTART: begin
ycount <= 0;
// set a default value (blank) for pixel output
// note: multiple non-blocking assignments are vendor-specific
gfx <= 0;
if (vstart) state <= WAIT_FOR_LOAD;
end
WAIT_FOR_LOAD: begin
xcount <= 0;
gfx <= 0;
if (load) state <= LOAD1_SETUP;
end
LOAD1_SETUP: begin
rom_addr <= ycount;
gfx <= 0;
state <= LOAD1_FETCH;
end
LOAD1_FETCH: begin
outbits[7:0] <= rom_bits;
gfx <= 0;
state <= WAIT_FOR_HSTART;
end
WAIT_FOR_HSTART: begin
if (hstart) state <= DRAW;
gfx <= 0;
end
DRAW: begin
// mirror graphics left/right
gfx <= outbits[xcount[3]?~xcount[2:0]:xcount[2:0]];
gfx <= outbits[xcount<8 ? xcount[2:0] : ~xcount[2:0]];
xcount <= xcount + 1;
if (xcount == 15) begin // pre-increment value
xactive <= 0; // done drawing this scanline
if (ycount == 0) // post-increment value
yactive <= 0; // done drawing sprite
ycount <= ycount + 1;
if (ycount == 15) // pre-increment value
state <= WAIT_FOR_VSTART; // done drawing sprite
else
state <= WAIT_FOR_LOAD; // done drawing this scanline
end
end
default: begin
state <= 0; // TODO: reset
gfx <= 0;
end
endcase
end
endmodule
module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
input clk;
@ -127,7 +146,7 @@ module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
.vstart(vstart),
.load(hsync),
.hstart(hstart),
.rom_yofs(car_sprite_yofs),
.rom_addr(car_sprite_yofs),
.rom_bits(car_sprite_bits),
.gfx(car_gfx),
.in_progress(unused));

View File

@ -159,6 +159,15 @@ function PixelEditor(parentDiv, fmt, palette, initialData, thumbnails) {
});
}
function setPixels(p) {
var i = 0;
for (var y=0; y<height; y++) {
for (var x=0; x<width; x++) {
setPixel(x, y, p[i++]);
}
}
}
this.rotate = function(deg) {
console.log("rotate " + deg);
var s1 = Math.sin(deg * Math.PI / 180);
@ -175,12 +184,35 @@ function PixelEditor(parentDiv, fmt, palette, initialData, thumbnails) {
p[i++] = col;
}
}
i = 0;
setPixels(p);
commit();
}
this.flipy = function() {
console.log("flipy");
var p = self.getImageColors();
var i = 0;
for (var y=0; y<height; y++) {
for (var x=0; x<width; x++) {
setPixel(x, y, p[i++]);
var col = getPixel(x, height-1-y);
p[i++] = col;
}
}
setPixels(p);
commit();
}
this.flipx = function() {
console.log("flipx");
var p = self.getImageColors();
var i = 0;
for (var y=0; y<height; y++) {
for (var x=0; x<width; x++) {
var col = getPixel(width-1-x, y);
p[i++] = col;
}
}
setPixels(p);
commit();
}
}
@ -472,6 +504,14 @@ function pixelEditorKeypress(e) {
case 36: // End
currentPixelEditor.rotate(45);
break;
}
switch (e.charCode) {
case 104:
currentPixelEditor.flipx();
break;
case 118:
currentPixelEditor.flipy();
break;
default:
console.log(e);
break;

View File

@ -297,6 +297,24 @@ var VerilogPlatform = function(mainElement, options) {
gen.__unreset();
}
function shadowText(ctx, txt, x, y) {
ctx.shadowColor = "black";
ctx.shadowBlur = 0;
ctx.shadowOffsetY = -1;
ctx.shadowOffsetX = 0;
ctx.fillText(txt, x, y);
ctx.shadowOffsetY = 1;
ctx.shadowOffsetX = 0;
ctx.fillText(txt, x, y);
ctx.shadowOffsetY = 0;
ctx.shadowOffsetX = -1;
ctx.fillText(txt, x, y);
ctx.shadowOffsetY = 0;
ctx.shadowOffsetX = 1;
ctx.fillText(txt, x, y);
ctx.shadowOffsetX = 0;
}
function updateScopeFrame() {
var arr = ports_and_signals;
if (!arr) return;
@ -348,17 +366,18 @@ var VerilogPlatform = function(mainElement, options) {
ctx.fillStyle = name == inspect_sym ? "yellow" : "white";
name = name.replace(/__DOT__/g,'.');
ctx.textAlign = 'left';
ctx.fillText(name, 1, yposlist[i]);
ctx.fillStyle = "white";
shadowText(ctx, name, 1, yposlist[i]);
if (scope_time_x > 0) {
ctx.textAlign = 'right';
var value = arr.length * scope_time_x + i + jstart;
ctx.fillText(""+trace_buffer[value], videoWidth-1, yposlist[i]);
shadowText(ctx, ""+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),
shadowText(ctx, ""+(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);
}