Starting work on PPU -> display -> VGA
This commit is contained in:
parent
bfcdec1d83
commit
3c3dbd2175
@ -1,7 +1,12 @@
|
|||||||
TB = tb_top
|
TB = tb_top
|
||||||
SOURCES = gb.sv cpu.sv ppu.sv idec.sv ctrl.sv alu.sv alu16.sv regbank.sv rom.sv ram.sv cart.sv tb_top.sv clkgen.sv
|
SOURCES = gb.sv cpu.sv ppu.sv idec.sv ctrl.sv alu.sv alu16.sv regbank.sv rom.sv ram.sv cart.sv display.sv tb_top.sv vgasim.sv clkgen.sv
|
||||||
INCLUDES = cpu_pkg.svh sva_common.svh
|
INCLUDES = cpu_pkg.svh sva_common.svh
|
||||||
PATH_SRC = ../rtl:../rtl/cpu:../rtl/ppu:../sim
|
|
||||||
|
DPI_SOURCES = vgasim.c
|
||||||
|
DPI_CFLAGS =
|
||||||
|
DPI_LIBS =
|
||||||
|
|
||||||
|
PATH_SRC = ../rtl:../rtl/cpu:../rtl/ppu:../sim:../dpi
|
||||||
|
|
||||||
DEFINES = SVA_ENABLE
|
DEFINES = SVA_ENABLE
|
||||||
|
|
||||||
|
113
dpi/vgasim.c
Normal file
113
dpi/vgasim.c
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#define WBUF_SIZE 32
|
||||||
|
|
||||||
|
static int fifo_fd;
|
||||||
|
|
||||||
|
#define VGASIM_CMD_INIT 0x01
|
||||||
|
#define VGASIM_CMD_RESET 0x02
|
||||||
|
#define VGASIM_CMD_TICK 0x03
|
||||||
|
|
||||||
|
int vgasim_init(int screen_h, int screen_w, int screen_bpp) {
|
||||||
|
int ret;
|
||||||
|
ssize_t bytes_written, bytes_written_total;
|
||||||
|
ssize_t pkt_size;
|
||||||
|
char wbuf[WBUF_SIZE];
|
||||||
|
uint16_t *wbuf_ptr;
|
||||||
|
printf("dpi: vgasim_init: %d x %d x %d\n", screen_h, screen_w, screen_bpp);
|
||||||
|
|
||||||
|
ret = open("/tmp/vgasim", O_WRONLY | O_NONBLOCK);
|
||||||
|
if (ret == -1) {
|
||||||
|
printf("dpi: vgasim_init: failed (%d)\n", errno);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fifo_fd = ret;
|
||||||
|
|
||||||
|
wbuf_ptr = (uint16_t*)wbuf;
|
||||||
|
|
||||||
|
*wbuf_ptr++ = VGASIM_CMD_INIT;
|
||||||
|
*wbuf_ptr++ = (uint16_t)screen_h;
|
||||||
|
*wbuf_ptr++ = (uint16_t)screen_w;
|
||||||
|
*wbuf_ptr++ = (uint16_t)screen_bpp;
|
||||||
|
|
||||||
|
pkt_size = 4 * sizeof(uint16_t);
|
||||||
|
|
||||||
|
bytes_written_total = 0;
|
||||||
|
do {
|
||||||
|
bytes_written = write(fifo_fd, wbuf + bytes_written_total, pkt_size - bytes_written_total);
|
||||||
|
if (bytes_written < 0) {
|
||||||
|
printf("dpi: vgasim_init: write error (%d): %s\n", errno, strerror(errno));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
bytes_written_total += bytes_written;
|
||||||
|
} while (bytes_written_total < pkt_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vgasim_reset() {
|
||||||
|
int ret;
|
||||||
|
ssize_t bytes_written, bytes_written_total;
|
||||||
|
ssize_t pkt_size;
|
||||||
|
char wbuf[WBUF_SIZE];
|
||||||
|
uint16_t *wbuf_ptr;
|
||||||
|
|
||||||
|
printf("dpi: vgasim_reset\n");
|
||||||
|
|
||||||
|
wbuf_ptr = (uint16_t*)wbuf;
|
||||||
|
*wbuf_ptr++ = VGASIM_CMD_RESET;
|
||||||
|
pkt_size = sizeof(uint16_t);
|
||||||
|
|
||||||
|
bytes_written_total = 0;
|
||||||
|
do {
|
||||||
|
bytes_written = write(fifo_fd, wbuf + bytes_written_total, pkt_size - bytes_written_total);
|
||||||
|
if (bytes_written < 0) {
|
||||||
|
printf("dpi: vgasim_init: write error (%d): %s\n", errno, strerror(errno));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
bytes_written_total += bytes_written;
|
||||||
|
} while (bytes_written_total < pkt_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vgasim_tick(int r, int g, int b, int hsync, int vsync) {
|
||||||
|
int ret;
|
||||||
|
ssize_t bytes_written, bytes_written_total;
|
||||||
|
ssize_t pkt_size;
|
||||||
|
char wbuf[WBUF_SIZE];
|
||||||
|
uint16_t *wbuf_ptr16;
|
||||||
|
uint8_t *wbuf_ptr8;
|
||||||
|
|
||||||
|
//printf("dpi: vgasim_tick: %X, %X, %X (%s%s)\n", r, g, b, hsync ? "h" : "", vsync ? "v" : "");
|
||||||
|
|
||||||
|
wbuf_ptr16 = (uint16_t*)wbuf;
|
||||||
|
*wbuf_ptr16++ = VGASIM_CMD_TICK;
|
||||||
|
wbuf_ptr8 = (uint8_t*)wbuf_ptr16;
|
||||||
|
*wbuf_ptr8++ = (uint8_t)r;
|
||||||
|
*wbuf_ptr8++ = (uint8_t)g;
|
||||||
|
*wbuf_ptr8++ = (uint8_t)b;
|
||||||
|
*wbuf_ptr8++ = (uint8_t)((vsync ? 0x02 : 0x00) | (hsync ? 0x01 : 0x00));
|
||||||
|
pkt_size = sizeof(uint16_t) + 4 * sizeof(uint8_t);
|
||||||
|
|
||||||
|
bytes_written_total = 0;
|
||||||
|
do {
|
||||||
|
bytes_written = write(fifo_fd, wbuf + bytes_written_total, pkt_size - bytes_written_total);
|
||||||
|
if (bytes_written < 0) {
|
||||||
|
printf("dpi: vgasim_init: write error (%d): %s\n", errno, strerror(errno));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
bytes_written_total += bytes_written;
|
||||||
|
} while (bytes_written_total < pkt_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
115
rtl/display.sv
Normal file
115
rtl/display.sv
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
module display #(
|
||||||
|
parameter VGA_DEPTH = 8,
|
||||||
|
parameter VGA_W = 640,
|
||||||
|
parameter VGA_H = 480
|
||||||
|
) (
|
||||||
|
input logic nreset,
|
||||||
|
|
||||||
|
input logic display_enable_i,
|
||||||
|
|
||||||
|
input logic ppu_clk,
|
||||||
|
input logic [ 1:0] ppu_pixel_i,
|
||||||
|
input logic ppu_vsync_i,
|
||||||
|
input logic ppu_hsync_i,
|
||||||
|
|
||||||
|
// RGB ppu_pixel
|
||||||
|
input logic [ 2:0][VGA_DEPTH-1:0] VGA_PALETTE[3:0],
|
||||||
|
|
||||||
|
input logic vga_clk,
|
||||||
|
output logic [VGA_DEPTH-1:0] vga_r_o,
|
||||||
|
output logic [VGA_DEPTH-1:0] vga_g_o,
|
||||||
|
output logic [VGA_DEPTH-1:0] vga_b_o,
|
||||||
|
output logic vga_hsync_o,
|
||||||
|
output logic vga_vsync_o
|
||||||
|
);
|
||||||
|
|
||||||
|
localparam DISPLAY_W = 160;
|
||||||
|
localparam DISPLAY_H = 144;
|
||||||
|
localparam BUFFER_SIZE = DISPLAY_W * DISPLAY_H;
|
||||||
|
|
||||||
|
logic [7:0] ppu_px_r;
|
||||||
|
logic [7:0] ppu_py_r;
|
||||||
|
logic [7:0] ppu_px_next;
|
||||||
|
logic [7:0] ppu_py_next;
|
||||||
|
|
||||||
|
logic [9:0] vga_px_r;
|
||||||
|
logic [9:0] vga_py_r;
|
||||||
|
logic [9:0] vga_px_next;
|
||||||
|
logic [9:0] vga_py_next;
|
||||||
|
|
||||||
|
logic vga_vsync;
|
||||||
|
logic vga_hsync;
|
||||||
|
|
||||||
|
always_ff @(posedge ppu_clk or negedge nreset) begin
|
||||||
|
if (!nreset) begin
|
||||||
|
ppu_px_r <= '0;
|
||||||
|
ppu_py_r <= '0;
|
||||||
|
end else begin
|
||||||
|
ppu_px_r <= ppu_px_next;
|
||||||
|
ppu_py_r <= ppu_py_next;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign ppu_px_next = ppu_hsync_i ? '0 : (ppu_px_r + 8'h01);
|
||||||
|
assign ppu_py_next = ppu_vsync_i ? '0 :
|
||||||
|
ppu_hsync_i ? (ppu_py_r + 8'h01) :
|
||||||
|
(ppu_py_r);
|
||||||
|
|
||||||
|
always_ff @(posedge vga_clk or negedge nreset) begin
|
||||||
|
if (!nreset) begin
|
||||||
|
vga_px_r <= '0;
|
||||||
|
vga_py_r <= '0;
|
||||||
|
end else begin
|
||||||
|
vga_px_r <= vga_px_next;
|
||||||
|
vga_py_r <= vga_py_next;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign vga_px_next = vga_hsync ? '0 : (vga_px_r + 8'h01);
|
||||||
|
assign vga_py_next = vga_vsync ? '0 :
|
||||||
|
vga_hsync ? (vga_py_r + 8'h01) :
|
||||||
|
(vga_py_r);
|
||||||
|
|
||||||
|
assign vga_hsync = (vga_px_r == VGA_W);
|
||||||
|
assign vga_vsync = (vga_py_r == VGA_H);
|
||||||
|
|
||||||
|
|
||||||
|
logic [1:0] buffer_r[BUFFER_SIZE-1:0];
|
||||||
|
logic [15:0] buffer_raddr;
|
||||||
|
logic [15:0] buffer_waddr;
|
||||||
|
|
||||||
|
logic buffer_we;
|
||||||
|
|
||||||
|
always_ff @(posedge ppu_clk or negedge nreset) begin
|
||||||
|
if (!nreset) begin
|
||||||
|
buffer_r[buffer_waddr] <= '0;
|
||||||
|
end else if (buffer_we) begin
|
||||||
|
buffer_r[buffer_waddr] <= ppu_pixel_i;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign buffer_we = ~ppu_vsync_i & ~ppu_hsync_i;
|
||||||
|
|
||||||
|
assign buffer_waddr = DISPLAY_W * ppu_py_r + ppu_px_r;
|
||||||
|
|
||||||
|
logic [1:0] buffer_rval;
|
||||||
|
|
||||||
|
always_ff @(posedge vga_clk or negedge nreset) begin
|
||||||
|
if (!nreset) begin
|
||||||
|
buffer_rval <= '0;
|
||||||
|
end else begin
|
||||||
|
buffer_rval <= buffer_r[buffer_raddr];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign buffer_raddr = DISPLAY_W * vga_py_r + vga_py_r;
|
||||||
|
|
||||||
|
logic vga_in_display;
|
||||||
|
assign vga_in_display = (vga_py_r < DISPLAY_H) & (vga_px_r < DISPLAY_W);
|
||||||
|
|
||||||
|
assign vga_r_o = (display_enable_i & vga_in_display) ? VGA_PALETTE[buffer_rval][0] : '0;
|
||||||
|
assign vga_g_o = (display_enable_i & vga_in_display) ? VGA_PALETTE[buffer_rval][1] : '0;
|
||||||
|
assign vga_b_o = (display_enable_i & vga_in_display) ? VGA_PALETTE[buffer_rval][2] : '0;
|
||||||
|
|
||||||
|
assign vga_vsync_o = vga_vsync;
|
||||||
|
assign vga_hsync_o = vga_hsync;
|
||||||
|
|
||||||
|
endmodule : display
|
19
rtl/gb.sv
19
rtl/gb.sv
@ -10,7 +10,13 @@ module gb (
|
|||||||
output wire cart_nwr_o,
|
output wire cart_nwr_o,
|
||||||
output wire cart_ncs_o,
|
output wire cart_ncs_o,
|
||||||
output wire [15:0] cart_addr_o,
|
output wire [15:0] cart_addr_o,
|
||||||
inout wire [ 7:0] cart_data_io
|
inout wire [ 7:0] cart_data_io,
|
||||||
|
|
||||||
|
// PPU output to display
|
||||||
|
output logic ppu_display_enable_o,
|
||||||
|
output logic [ 1:0] ppu_pixel_o,
|
||||||
|
output logic ppu_vsync_o,
|
||||||
|
output logic ppu_hsync_o
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -24,13 +30,10 @@ logic [ 7:0] cpu_ppu_rdata;
|
|||||||
logic rom_enable_r;
|
logic rom_enable_r;
|
||||||
logic rom_sel;
|
logic rom_sel;
|
||||||
logic [ 7:0] rom_rdata;
|
logic [ 7:0] rom_rdata;
|
||||||
|
|
||||||
logic hiram_sel;
|
logic hiram_sel;
|
||||||
logic [ 7:0] hiram_rdata;
|
logic [ 7:0] hiram_rdata;
|
||||||
|
|
||||||
logic vram_sel;
|
logic vram_sel;
|
||||||
logic [ 7:0] vram_rdata;
|
logic [ 7:0] vram_rdata;
|
||||||
|
|
||||||
logic cart_sel;
|
logic cart_sel;
|
||||||
logic [ 7:0] cart_rdata;
|
logic [ 7:0] cart_rdata;
|
||||||
|
|
||||||
@ -48,10 +51,16 @@ ppu ppu_inst (
|
|||||||
.clk (clk),
|
.clk (clk),
|
||||||
.nreset (nreset),
|
.nreset (nreset),
|
||||||
|
|
||||||
|
.display_enable_o(ppu_display_enable_o),
|
||||||
|
|
||||||
.cpu_addr_i (cpu_addr),
|
.cpu_addr_i (cpu_addr),
|
||||||
.cpu_rdata_o(cpu_ppu_rdata),
|
.cpu_rdata_o(cpu_ppu_rdata),
|
||||||
.cpu_we_i (cpu_we),
|
.cpu_we_i (cpu_we),
|
||||||
.cpu_wdata_i(cpu_wdata)
|
.cpu_wdata_i(cpu_wdata),
|
||||||
|
|
||||||
|
.pixel_o (ppu_pixel_o),
|
||||||
|
.vsync_o (ppu_vsync_o),
|
||||||
|
.hsync_o (ppu_hsync_o)
|
||||||
);
|
);
|
||||||
|
|
||||||
assign rom_enable_r = '1;
|
assign rom_enable_r = '1;
|
||||||
|
@ -4,10 +4,16 @@ module ppu (
|
|||||||
input logic clk,
|
input logic clk,
|
||||||
input logic nreset,
|
input logic nreset,
|
||||||
|
|
||||||
|
output logic display_enable_o,
|
||||||
|
|
||||||
input logic [15:0] cpu_addr_i,
|
input logic [15:0] cpu_addr_i,
|
||||||
output logic [ 7:0] cpu_rdata_o,
|
output logic [ 7:0] cpu_rdata_o,
|
||||||
input logic cpu_we_i,
|
input logic cpu_we_i,
|
||||||
input logic [ 7:0] cpu_wdata_i
|
input logic [ 7:0] cpu_wdata_i,
|
||||||
|
|
||||||
|
output logic [ 1:0] pixel_o,
|
||||||
|
output logic vsync_o,
|
||||||
|
output logic hsync_o
|
||||||
);
|
);
|
||||||
|
|
||||||
`define IOREG_DECL(name) \
|
`define IOREG_DECL(name) \
|
||||||
@ -24,7 +30,7 @@ module ppu (
|
|||||||
name``_r <= name``_next; \
|
name``_r <= name``_next; \
|
||||||
end \
|
end \
|
||||||
assign name``_sel = (cpu_addr_i == addr); \
|
assign name``_sel = (cpu_addr_i == addr); \
|
||||||
assign name``_we = name``_we & cpu_we_i;
|
assign name``_we = name``_sel & cpu_we_i;
|
||||||
|
|
||||||
typedef struct packed {
|
typedef struct packed {
|
||||||
logic lcd_en;
|
logic lcd_en;
|
||||||
@ -93,6 +99,14 @@ assign cpu_rdata_o =
|
|||||||
`endif
|
`endif
|
||||||
{8{ sy_sel}} & sy_r;
|
{8{ sy_sel}} & sy_r;
|
||||||
|
|
||||||
|
assign pixel_o = ((lx_r < 8'd160) & (ly_r < 8'd144)) ? ly_r[1:0] : // should create a nice line-pattern for now
|
||||||
|
2'h00;
|
||||||
|
|
||||||
|
assign vsync_o = ly_r >= 8'd144;
|
||||||
|
assign hsync_o = lx_r >= 8'd160;
|
||||||
|
|
||||||
|
assign display_enable_o = lcdc_r[7];
|
||||||
|
|
||||||
`undef IOREG_DEF
|
`undef IOREG_DEF
|
||||||
`undef IOREG_DECL
|
`undef IOREG_DECL
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
module tb_top;
|
module tb_top;
|
||||||
|
|
||||||
logic clk;
|
logic gb_clk;
|
||||||
|
logic vga_clk;
|
||||||
|
|
||||||
|
logic gb_nreset;
|
||||||
|
logic vga_nreset;
|
||||||
logic nreset;
|
logic nreset;
|
||||||
|
|
||||||
wire gb_cart_clk;
|
wire gb_cart_clk;
|
||||||
@ -11,13 +15,39 @@ wire gb_cart_ncs;
|
|||||||
wire [15:0] gb_cart_addr;
|
wire [15:0] gb_cart_addr;
|
||||||
wire [ 7:0] gb_cart_data;
|
wire [ 7:0] gb_cart_data;
|
||||||
|
|
||||||
clkgen clkgen_inst (
|
wire display_enable;
|
||||||
.clk (clk),
|
wire [ 1:0] ppu_pixel;
|
||||||
.nreset(nreset)
|
wire ppu_hsync;
|
||||||
|
wire ppu_vsync;
|
||||||
|
|
||||||
|
logic [7:0] vga_r;
|
||||||
|
logic [7:0] vga_g;
|
||||||
|
logic [7:0] vga_b;
|
||||||
|
logic vga_hsync;
|
||||||
|
logic vga_vsync;
|
||||||
|
|
||||||
|
// 4.19MHz
|
||||||
|
clkgen #(
|
||||||
|
.PERIOD_NS (238),
|
||||||
|
.RESET_DELAY_NS (400)
|
||||||
|
) gb_clkgen_inst (
|
||||||
|
.clk (gb_clk),
|
||||||
|
.nreset(gb_nreset)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 25MHz
|
||||||
|
clkgen #(
|
||||||
|
.PERIOD_NS (40),
|
||||||
|
.RESET_DELAY_NS (70)
|
||||||
|
) vga_clkgen_inst (
|
||||||
|
.clk (vga_clk),
|
||||||
|
.nreset(vga_nreset)
|
||||||
|
);
|
||||||
|
|
||||||
|
assign nreset = gb_nreset & vga_nreset;
|
||||||
|
|
||||||
gb gb_inst (
|
gb gb_inst (
|
||||||
.clk (clk),
|
.clk (gb_clk),
|
||||||
.nreset(nreset),
|
.nreset(nreset),
|
||||||
|
|
||||||
.cart_clk_o (gb_cart_clk),
|
.cart_clk_o (gb_cart_clk),
|
||||||
@ -26,7 +56,12 @@ gb gb_inst (
|
|||||||
.cart_nwr_o (gb_cart_nwr),
|
.cart_nwr_o (gb_cart_nwr),
|
||||||
.cart_ncs_o (gb_cart_ncs),
|
.cart_ncs_o (gb_cart_ncs),
|
||||||
.cart_addr_o (gb_cart_addr),
|
.cart_addr_o (gb_cart_addr),
|
||||||
.cart_data_io (gb_cart_data)
|
.cart_data_io (gb_cart_data),
|
||||||
|
|
||||||
|
.ppu_display_enable_o(display_enable),
|
||||||
|
.ppu_pixel_o (ppu_pixel),
|
||||||
|
.ppu_vsync_o (ppu_vsync),
|
||||||
|
.ppu_hsync_o (ppu_hsync)
|
||||||
);
|
);
|
||||||
|
|
||||||
cart cart_inst (
|
cart cart_inst (
|
||||||
@ -39,10 +74,45 @@ cart cart_inst (
|
|||||||
.data_io (gb_cart_data)
|
.data_io (gb_cart_data)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
display display_inst (
|
||||||
|
.nreset (nreset),
|
||||||
|
.VGA_PALETTE(
|
||||||
|
// RR GG BB
|
||||||
|
'{24'h00_00_00, // colour 00
|
||||||
|
24'h60_60_60, // colour 01
|
||||||
|
24'hA0_A0_A0, // colour 10
|
||||||
|
24'hFF_FF_FF} // colour 11
|
||||||
|
),
|
||||||
|
|
||||||
|
.display_enable_i(display_enable),
|
||||||
|
|
||||||
|
.ppu_clk (gb_clk),
|
||||||
|
.ppu_pixel_i(ppu_pixel),
|
||||||
|
.ppu_vsync_i(ppu_vsync),
|
||||||
|
.ppu_hsync_i(ppu_hsync),
|
||||||
|
|
||||||
|
.vga_clk (vga_clk),
|
||||||
|
.vga_r_o (vga_r),
|
||||||
|
.vga_g_o (vga_g),
|
||||||
|
.vga_b_o (vga_b),
|
||||||
|
.vga_hsync_o(vga_hsync),
|
||||||
|
.vga_vsync_o(vga_vsync)
|
||||||
|
);
|
||||||
|
|
||||||
|
vgasim vgasim_inst (
|
||||||
|
.clk (vga_clk),
|
||||||
|
.nreset(nreset),
|
||||||
|
|
||||||
|
.r_i (vga_r),
|
||||||
|
.g_i (vga_g),
|
||||||
|
.b_i (vga_b),
|
||||||
|
.hsync_i (vga_hsync),
|
||||||
|
.vsync_i (vga_vsync)
|
||||||
|
);
|
||||||
|
|
||||||
`ifdef SVA_ENABLE
|
`ifdef SVA_ENABLE
|
||||||
`include "sva_common.svh"
|
`include "sva_common.svh"
|
||||||
`SVA_DEF_CLK(clk);
|
`SVA_DEF_CLK(gb_clk);
|
||||||
`SVA_DEF_NRESET(nreset);
|
`SVA_DEF_NRESET(nreset);
|
||||||
|
|
||||||
logic instr_valid;
|
logic instr_valid;
|
||||||
@ -70,14 +140,14 @@ assign vram_sel = gb_inst.vram_sel;
|
|||||||
assign hiram_sel = gb_inst.hiram_sel;
|
assign hiram_sel = gb_inst.hiram_sel;
|
||||||
assign ppu_sel = gb_inst.ppu_inst.sva_ppu_sel;
|
assign ppu_sel = gb_inst.ppu_inst.sva_ppu_sel;
|
||||||
|
|
||||||
always_ff @(posedge clk or negedge nreset) begin
|
always_ff @(posedge gb_clk or negedge nreset) begin
|
||||||
if (!nreset)
|
if (!nreset)
|
||||||
current_pc <= '0;
|
current_pc <= '0;
|
||||||
else if (instr_valid)
|
else if (instr_valid)
|
||||||
current_pc <= gb_inst.cpu_inst.pc_r;
|
current_pc <= gb_inst.cpu_inst.pc_r;
|
||||||
end
|
end
|
||||||
|
|
||||||
always_ff @(posedge clk or negedge nreset) begin
|
always_ff @(posedge gb_clk or negedge nreset) begin
|
||||||
if (!nreset) begin
|
if (!nreset) begin
|
||||||
last_write_address <= '0;
|
last_write_address <= '0;
|
||||||
last_write_value <= '0;
|
last_write_value <= '0;
|
||||||
@ -108,7 +178,7 @@ localparam MAX_COUNT = 10000;
|
|||||||
logic [15:0] pc_history [$];
|
logic [15:0] pc_history [$];
|
||||||
int unsigned pc_history_count [$];
|
int unsigned pc_history_count [$];
|
||||||
|
|
||||||
always @(posedge clk iff instr_valid) begin
|
always @(posedge gb_clk iff instr_valid) begin
|
||||||
automatic int unsigned current_pc_count = 0;
|
automatic int unsigned current_pc_count = 0;
|
||||||
|
|
||||||
foreach (pc_history[i]) begin
|
foreach (pc_history[i]) begin
|
||||||
|
44
sim/vgasim.sv
Normal file
44
sim/vgasim.sv
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
module vgasim #(
|
||||||
|
parameter SCREEN_H = 640,
|
||||||
|
parameter SCREEN_W = 480,
|
||||||
|
parameter DEPTH = 8
|
||||||
|
) (
|
||||||
|
input logic clk,
|
||||||
|
input logic nreset,
|
||||||
|
|
||||||
|
input logic [DEPTH-1:0] r_i,
|
||||||
|
input logic [DEPTH-1:0] g_i,
|
||||||
|
input logic [DEPTH-1:0] b_i,
|
||||||
|
input logic hsync_i,
|
||||||
|
input logic vsync_i
|
||||||
|
);
|
||||||
|
|
||||||
|
import "DPI-C" function int vgasim_init(input int screen_h, input int screen_w, input int screen_bpp);
|
||||||
|
import "DPI-C" function int vgasim_reset();
|
||||||
|
import "DPI-C" function int vgasim_tick(input int r, input int g, input int b, input int hsync, input int vsync);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
automatic int ret = vgasim_init(SCREEN_W, SCREEN_H, DEPTH);
|
||||||
|
assert (ret == 0) else begin
|
||||||
|
$display($sformatf("[%0t] vgasim init failed: %d", $time, ret));
|
||||||
|
$fatal();
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(negedge nreset) begin
|
||||||
|
automatic int ret = vgasim_reset();
|
||||||
|
assert (ret == 0) else begin
|
||||||
|
$display($sformatf("[%0t] vgasim reset failed: %d", $time, ret));
|
||||||
|
$fatal();
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk iff nreset) begin
|
||||||
|
automatic int ret = vgasim_tick(r_i, g_i, b_i, hsync_i, vsync_i);
|
||||||
|
assert (ret == 0) else begin
|
||||||
|
$display($sformatf("[%0t] vgasim tick failed: %d", $time, ret));
|
||||||
|
$fatal();
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule : vgasim
|
BIN
src/vgasim
Executable file
BIN
src/vgasim
Executable file
Binary file not shown.
210
src/vgasim.c
Normal file
210
src/vgasim.c
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define RBUF_SIZE 256
|
||||||
|
|
||||||
|
#define DEF_SCREEN_W 640
|
||||||
|
#define DEF_SCREEN_H 480
|
||||||
|
|
||||||
|
unsigned int pixel_x;
|
||||||
|
unsigned int pixel_y;
|
||||||
|
unsigned int screen_w;
|
||||||
|
unsigned int screen_h;
|
||||||
|
SDL_Window* sdl_window;
|
||||||
|
SDL_Renderer* sdl_renderer;
|
||||||
|
SDL_Texture* sdl_texture;
|
||||||
|
|
||||||
|
Uint32* screen;
|
||||||
|
|
||||||
|
#define VGASIM_CMD_INIT 0x01
|
||||||
|
#define VGASIM_CMD_RESET 0x02
|
||||||
|
#define VGASIM_CMD_TICK 0x03
|
||||||
|
|
||||||
|
void vgasim_init(int new_screen_h, int new_screen_w, int bpp) {
|
||||||
|
printf("vgasim_init: %d x %d x %d\n", new_screen_w, new_screen_h, bpp);
|
||||||
|
pixel_x = 0;
|
||||||
|
pixel_y = 0;
|
||||||
|
|
||||||
|
if (screen_h != new_screen_h || screen_w != new_screen_w) {
|
||||||
|
printf("Need to resize\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
screen_h = new_screen_h;
|
||||||
|
screen_w = new_screen_w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vgasim_reset() {
|
||||||
|
printf("vgasim_reset\n");
|
||||||
|
pixel_x = 0;
|
||||||
|
pixel_y = 0;
|
||||||
|
SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(sdl_renderer);
|
||||||
|
SDL_RenderPresent(sdl_renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vgasim_tick(int r, int g, int b, int hsync, int vsync) {
|
||||||
|
//printf("vgasim_tick: %X, %X, %X (%s%s)\n", r, g, b, hsync ? "h" : "", vsync ? "v" : "");
|
||||||
|
|
||||||
|
screen[screen_w * pixel_y + pixel_x] = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) << 0);
|
||||||
|
pixel_x++;
|
||||||
|
if (hsync || pixel_x > screen_w) {
|
||||||
|
pixel_x = 0;
|
||||||
|
pixel_y++;
|
||||||
|
//printf("vgasim_tick: vsync\n");
|
||||||
|
SDL_UpdateTexture(sdl_texture, NULL, screen, screen_w * sizeof (Uint32));
|
||||||
|
SDL_RenderClear(sdl_renderer);
|
||||||
|
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
|
||||||
|
SDL_RenderPresent(sdl_renderer);
|
||||||
|
}
|
||||||
|
if (vsync || pixel_y > screen_h) {
|
||||||
|
pixel_y = 0;
|
||||||
|
// printf("vgasim_tick: hsync\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t cmd_parse(char *buf, ssize_t bufsize) {
|
||||||
|
uint8_t *p8;
|
||||||
|
uint16_t *p16;
|
||||||
|
uint16_t cmd;
|
||||||
|
uint8_t r, g, b, hvsync;
|
||||||
|
uint16_t h, w, bpp;
|
||||||
|
|
||||||
|
if (bufsize < sizeof(uint16_t))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
p16 = (uint16_t*)buf;
|
||||||
|
cmd = *p16++;
|
||||||
|
p8 = (uint8_t*)p16;
|
||||||
|
switch (cmd) {
|
||||||
|
case VGASIM_CMD_INIT:
|
||||||
|
if (bufsize < 4 * sizeof(uint16_t))
|
||||||
|
return 0;
|
||||||
|
h = *p16++;
|
||||||
|
w = *p16++;
|
||||||
|
bpp = *p16++;
|
||||||
|
vgasim_init(h, w, bpp);
|
||||||
|
return 4 * sizeof(uint16_t);
|
||||||
|
|
||||||
|
case VGASIM_CMD_RESET:
|
||||||
|
vgasim_reset();
|
||||||
|
return sizeof(uint16_t);
|
||||||
|
|
||||||
|
case VGASIM_CMD_TICK:
|
||||||
|
if (bufsize < sizeof(uint16_t) + 4 * sizeof(uint8_t))
|
||||||
|
return 0;
|
||||||
|
r = *p8++;
|
||||||
|
g = *p8++;
|
||||||
|
b = *p8++;
|
||||||
|
hvsync = *p8++;
|
||||||
|
vgasim_tick(r, g, b, (hvsync & 0x01), ((hvsync & 0x02) >> 1));
|
||||||
|
|
||||||
|
return sizeof(uint16_t) + 4 * sizeof(uint8_t);
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("Unsupported command packet received: %X\n", cmd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char **argv) {
|
||||||
|
int fifo_fd;
|
||||||
|
char rbuf[RBUF_SIZE];
|
||||||
|
ssize_t bytes_read, bytes_read_total;
|
||||||
|
ssize_t bytes_parsed, bytes_parsed_total;
|
||||||
|
int ret = mkfifo("/tmp/vgasim", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||||
|
|
||||||
|
if (ret == -1 && errno != EEXIST) {
|
||||||
|
perror(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SDL_Init(SDL_INIT_VIDEO);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("SDL_Init failed: %d\n", ret);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdl_window = SDL_CreateWindow("vgasim", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DEF_SCREEN_W, DEF_SCREEN_H, 0);
|
||||||
|
if (sdl_window == NULL) {
|
||||||
|
printf("SDL_CreateWindow failed\n");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
|
||||||
|
if (sdl_renderer == NULL) {
|
||||||
|
printf("SDL_CreateRenderer failed\n");
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, DEF_SCREEN_W, DEF_SCREEN_H);
|
||||||
|
if (sdl_texture == NULL) {
|
||||||
|
printf("SDL_CreateTexture failed\n");
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(sdl_renderer);
|
||||||
|
SDL_RenderPresent(sdl_renderer);
|
||||||
|
|
||||||
|
screen = malloc(DEF_SCREEN_H * DEF_SCREEN_W * sizeof(Uint32));
|
||||||
|
if (screen == NULL) {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Listening for incoming connections...\n");
|
||||||
|
ret = open("/tmp/vgasim", O_RDONLY);
|
||||||
|
if (ret == -1) {
|
||||||
|
perror(argv[0]);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
fifo_fd = ret;
|
||||||
|
|
||||||
|
bytes_read_total = 0;
|
||||||
|
do {
|
||||||
|
|
||||||
|
// Read as much bytes as we can into our buffer
|
||||||
|
bytes_read = read(fifo_fd, rbuf + bytes_read_total, RBUF_SIZE - bytes_read_total);
|
||||||
|
bytes_read_total += bytes_read;
|
||||||
|
|
||||||
|
bytes_parsed_total = 0;
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
do {
|
||||||
|
|
||||||
|
// Parse as many bytes as we can
|
||||||
|
bytes_parsed = cmd_parse(rbuf + bytes_parsed_total, bytes_read_total - bytes_parsed_total);
|
||||||
|
bytes_parsed_total += bytes_parsed;
|
||||||
|
} while (bytes_parsed > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's any data left unparsed, it's the start of a new incomplete packet.
|
||||||
|
// Copy it to the front of the buffer and restart from there.
|
||||||
|
// Otherwise we cleared our entire buffer and can start again from the beginning.
|
||||||
|
if (bytes_read_total > bytes_parsed_total) {
|
||||||
|
memcpy(rbuf, rbuf + bytes_parsed_total, (bytes_read_total - bytes_parsed_total));
|
||||||
|
bytes_read_total = bytes_read_total - bytes_parsed_total;
|
||||||
|
} else {
|
||||||
|
bytes_read_total = 0;
|
||||||
|
}
|
||||||
|
} while (bytes_read > 0);
|
||||||
|
|
||||||
|
if (bytes_read < 0) {
|
||||||
|
perror(argv[0]);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(screen);
|
||||||
|
SDL_DestroyTexture(sdl_texture);
|
||||||
|
SDL_DestroyRenderer(sdl_renderer);
|
||||||
|
SDL_DestroyWindow(sdl_window);
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
Subproject commit 1486728ced51acd63f20caf41cc47b44f2efa436
|
Subproject commit cc619981f0804ce1da54a12d67ff4009d2df56b9
|
Loading…
Reference in New Issue
Block a user