diff --git a/.gitignore b/.gitignore index 765db6a..5cf5a36 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ pcomp/pcomp pcomp/sasm pcomp/sdis tridoraemu/tridoraemu +tridoracpu/build **/tridoracpu.cache/ **/tridoracpu.hw/ **/tridoracpu.ip_user_files/ diff --git a/tridoracpu/Makefile b/tridoracpu/Makefile index 96fc30f..fa7737c 100644 --- a/tridoracpu/Makefile +++ b/tridoracpu/Makefile @@ -18,19 +18,20 @@ BITSTREAM = build/$(TOP)_00.cfg.bit srcs = \ $(SRCDIR)/bram_tdp.v \ - $(SRCDIR)/dram_bridge.v \ $(SRCDIR)/fifo.v \ $(SRCDIR)/irqctrl.v \ - $(SRCDIR)/mem.v \ $(SRCDIR)/palette.v \ $(SRCDIR)/sdspi.v \ $(SRCDIR)/stackcpu.v \ $(SRCDIR)/stack.v \ - $(SRCDIR)/top.v \ $(SRCDIR)/uart.v \ $(SRCDIR)/vgafb.v -#srcs += $(SRCDIR)/ccgma1_clocks.v +# for CCGMA1-EVB +srcs += $(SRCDIR)/cpuclk_ccgm.v $(SRCDIR)/top_ccgm.v $(SRCDIR)/mem_ccgm.v + +# for Arty-A7 +# src += $(SRCDIR)/cpuclk.v $(SRCDIR)/top.v $(SRCDIR)/mem.v all: build synth impl clean: @@ -49,7 +50,7 @@ $(SYNTHFILE): $(srcs) $(YOSYS) -ql build/synth.log -p 'read -sv $(srcs); synth_gatemate -top $(TOP) -nomx8 -vlog $(SYNTHFILE)' $(BITSTREAM): $(SYNTHFILE) - $(PNR) -v -i build/$(SYNTHFILE) -o $(TOP) $(PNRFLAGS) >build/$@.log + $(PNR) -v -i $(SYNTHFILE) -o build/$(TOP) $(PNRFLAGS) >$@.log prog: $(BITSTREAM) $(OFL) $(OFLFLAGS) --bitstream $(BITSTREAM) diff --git a/tridoracpu/tridoracpu.srcs/cpuclk_ccgm.v b/tridoracpu/tridoracpu.srcs/cpuclk_ccgm.v new file mode 100644 index 0000000..37883d5 --- /dev/null +++ b/tridoracpu/tridoracpu.srcs/cpuclk_ccgm.v @@ -0,0 +1,41 @@ +`timescale 1ns / 1ps + +module cpu_clkgen( + input wire rst, + input wire clk10, + output wire cpuclk, + output wire locked + ); + + wire usr_pll_lock_stdy; + wire usr_pll_lock; + wire usr_ref_out; + + wire clk_nobuf; + + assign locked = usr_pll_lock; + + CC_PLL #( + .REF_CLK("10.0"), + .OUT_CLK("25.0"), + .LOCK_REQ("1"), + .PERF_MD("SPEED"), + .LOW_JITTER(1), + .CI_FILTER_CONST(2), + .CP_FILTER_CONST(4) + ) pll_cpuclk ( + .CLK_REF(clk10), + .CLK_FEEDBACK(1'b0), + .USR_CLK_REF(1'b0), + .USR_LOCKED_STDY_RST(1'b0), + .USR_PLL_LOCKED_STDY(usr_pll_lock_stdy), + .USR_PLL_LOCKED(usr_pll_lock), + .CLK0(clk_nobuf), + .CLK_REF_OUT(usr_ref_out) + ); + + CC_BUFG pll_bufg ( + .I(clk_nobuf), + .O(cpuclk) + ); +endmodule diff --git a/tridoracpu/tridoracpu.srcs/mem_ccgm.v b/tridoracpu/tridoracpu.srcs/mem_ccgm.v new file mode 100644 index 0000000..7f1e36f --- /dev/null +++ b/tridoracpu/tridoracpu.srcs/mem_ccgm.v @@ -0,0 +1,129 @@ +`timescale 1ns / 1ps + +// 32 bit wide rom with byte addressing (address bits 1-0 are ignored) +module rom32 #(parameter ADDR_WIDTH = 11, DATA_WIDTH = 32) +( + input wire clk, + input wire [ADDR_WIDTH-1:0] addr, + output reg [DATA_WIDTH-1:0] data_out, + input wire read_enable + ); + + wire [ADDR_WIDTH-2:0] internal_addr = addr[ADDR_WIDTH-1:2]; // -> ignore bit 0 + reg [DATA_WIDTH-1:0] rom [0:(2**(ADDR_WIDTH-2))-1]; + + initial begin + $readmemb("rom.mem", rom); + end + + always @(posedge clk) data_out <= rom[internal_addr]; + +endmodule + +module ram32 #(parameter ADDR_WIDTH = 16, DATA_WIDTH = 32) + +( + input wire clk, + input wire [ADDR_WIDTH-1:0] addr, + output reg [DATA_WIDTH-1:0] data_out, + input wire read_enable, + input wire [DATA_WIDTH-1:0] data_in, + input wire write_enable + ); + + reg [DATA_WIDTH-1:0] ram [0:(2**(ADDR_WIDTH-2))-1]; // 32bit words with byte addressing + wire [ADDR_WIDTH-2:0] internal_addr = addr[ADDR_WIDTH-1:2]; // -> ignore bit 1-0 + + always @(posedge clk) + begin + if(read_enable) + data_out <= ram[internal_addr]; + if(write_enable) + ram[internal_addr] <= data_in; + end +endmodule + +module mem #(parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32) +( + input wire clk, rst_n, + input wire [ADDR_WIDTH-1:0] addr, + output wire [DATA_WIDTH-1:0] data_out, + input wire read_enable, + input wire [DATA_WIDTH-1:0] data_in, + input wire write_enable, + output wire io_enable, + input wire [DATA_WIDTH-1:0] io_rd_data, + output wire mem_wait + ); + + wire [DATA_WIDTH-1:0] ram_out, rom_out, dram_out; + + // address map: + // ROM $0000 - $07FF 2K + // IO $0800 - $0FFF 2K + // RAM1 $1000 - $FFFF 60K + + wire ram_cs = addr[ADDR_WIDTH-1:12] != { {(ADDR_WIDTH-12){1'b0}}}; + wire ram1_cs = ram_cs && (addr[ADDR_WIDTH-1:16] == { {(ADDR_WIDTH-16){1'b0}}}); + wire ram2_cs = ram_cs && !ram1_cs; + wire rom_cs = !ram_cs && addr[11] == 1'b0; + wire io_cs = !ram_cs && addr[11] == 1'b1; + + assign io_enable = io_cs; + + wire ram_read = ram1_cs && read_enable; + wire ram_write = ram1_cs && write_enable; + wire rom_read = rom_cs && read_enable; + + reg [DATA_WIDTH-1:0] data_buf; + + localparam SEL_RAM1 = 0; + localparam SEL_RAM2 = 1; + localparam SEL_ROM = 2; + localparam SEL_IO = 3; + localparam SEL_ERR = 4; + + reg [1:0] out_sel; + + // test + reg [1:0] wait_state; + wire dram_wait = 0; + + ram32 #(.ADDR_WIDTH(16)) ram0 // 64KB RAM + ( + .clk(clk), + .addr(addr[15:0]), + .data_out(ram_out), + .read_enable(ram_read), + .data_in(data_in), + .write_enable(ram_write) + ); + + rom32 #(.ADDR_WIDTH(11)) rom0 // 2KB ROM + ( + .clk(clk), + .addr(addr[10:0]), + .data_out(rom_out), + .read_enable(rom_read) + ); + + assign data_out = (out_sel == SEL_RAM1 ) ? ram_out : + (out_sel == SEL_RAM2 ) ? dram_out : + (out_sel == SEL_ROM ) ? rom_out : + (out_sel == SEL_IO ) ? io_rd_data : + data_buf; + + assign mem_wait = ram2_cs && dram_wait; + + always @(posedge clk) + begin + data_buf <= data_out; + if(read_enable) out_sel <= + ram1_cs ? SEL_RAM1 : + ram2_cs ? SEL_RAM2: + rom_cs ? SEL_ROM : + io_cs ? SEL_IO : + SEL_ERR; + end +endmodule diff --git a/tridoracpu/tridoracpu.srcs/top_ccgm.v b/tridoracpu/tridoracpu.srcs/top_ccgm.v new file mode 100644 index 0000000..9555fdf --- /dev/null +++ b/tridoracpu/tridoracpu.srcs/top_ccgm.v @@ -0,0 +1,242 @@ +`timescale 1ns / 1ps + +`define clock cpuclk +`define clkfreq 25000000 +//`define clock clk +//`define clkfreq 100000000 +//`define clock clk_1hz +`define ENABLE_VGAFB +`define ENABLE_MICROSD + +module top( + input wire clk, + input wire rst, + output wire led0, + input wire uart_txd_in, + output wire uart_rxd_out + +`ifdef ENABLE_VGAFB + , + output wire [3:0] VGA_R, + output wire [3:0] VGA_G, + output wire [3:0] VGA_B, + + output wire VGA_HS_O, + output wire VGA_VS_O +`endif + +`ifdef ENABLE_MICROSD + , + output wire sd_cs_n, + output wire sd_mosi, + input wire sd_miso, + output wire sd_sck, + input wire sd_cd +`endif +); + + reg [31:0] counter; + + localparam ADDR_WIDTH = 32, WIDTH = 32, + ROMADDR_WIDTH = 11, IOADDR_WIDTH = 11, IOADDR_SEL = 4; + + wire [ADDR_WIDTH-1:0] mem_addr; + wire [WIDTH-1:0] mem_read_data; + wire [WIDTH-1:0] mem_write_data; + (* KEEP *) wire mem_wait; + + (* KEEP *) wire mem_read_enable; + (* KEEP *) wire mem_write_enable; + (* KEEP *) wire io_enable; + wire [WIDTH-1:0] io_rd_data; + wire [IOADDR_SEL-1:0] io_slot = mem_addr[IOADDR_WIDTH-1:IOADDR_WIDTH-IOADDR_SEL]; + + wire irq; + + // assign led0 = mem_wait; + + wire [WIDTH-1:0] debug_data1, debug_data2, + debug_data3, debug_data4, + debug_data5, debug_data6; + + assign led0 = debug_data6[0]; + + wire cpuclk, cpuclk_locked; + wire dram_refclk200; + wire pixclk = cpuclk; + cpu_clkgen cpuclk_0(~rst, clk, cpuclk, cpuclk_locked); + + mem #(.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(WIDTH)) mem0( + .clk(`clock), .rst_n(rst), .addr(mem_addr), + .data_out(mem_read_data), .read_enable(mem_read_enable), + .data_in(mem_write_data), .write_enable(mem_write_enable), + .io_enable(io_enable), + .io_rd_data(io_rd_data), + .mem_wait(mem_wait) + ); + +`ifdef ENABLE_VGAFB + localparam FB_ADDR_WIDTH = 14; + wire [FB_ADDR_WIDTH-1:0] fb_rd_addr; + wire [FB_ADDR_WIDTH-1:0] fb_wr_addr; + wire [WIDTH-1:0] fb_rd_data; + wire [WIDTH-1:0] fb_wr_data; + wire fb_rd_en, fb_wr_en; + + wire fb_cs_en = io_enable && (io_slot == 2); + + assign fb_rd_en = fb_cs_en && mem_read_enable; + assign fb_wr_en = fb_cs_en && mem_write_enable; + assign fb_wr_data = mem_write_data; + + vgafb vgafb0(`clock, pixclk, rst, + mem_addr[3:0], fb_rd_data, fb_wr_data, + fb_rd_en, fb_wr_en, + VGA_HS_O, VGA_VS_O, VGA_R, VGA_G, VGA_B); +`endif + + // SPI SD card controller ------------------------------------------------------------------- +`ifdef ENABLE_MICROSD + wire [7:0] spi_tx_data; + (*KEEP*) wire [7:0] spi_rx_data; + wire spi_tx_ready; // ready to transmit new data + wire spi_tx_empty; // tx fifo is empty + wire spi_rx_avail; // a byte has been received + wire spi_rx_ovr; // receiver overrun + wire spi_tx_write; // write strobe + wire spi_rx_read; // read strobe (clears rx_avail) + + wire spi_card_detect; // true is card is present + wire spi_card_changed; // card_detect signal has changed + wire spi_card_busy; // card is busy (MISO/DO is 0) + + wire spi_ctrl_write; // set the following flags + wire spi_rx_filter_en; // set to wait for start bit (1-to-0) when receiving + wire spi_txrx_en; // enable transmitter and receiver + wire spi_sclk_f_en; // enable spi clock without transceiver + wire spi_sclk_div_wr; // set clock divider from tx_data + + wire spi_cs; // cs signal for spi controller + wire [WIDTH-1:0] spi_rd_data; + + assign spi_cs = io_enable && (io_slot == 1); + + // spi read data: [ 0,...,0,cd,cc,cb,tr,te,ra,ro,d,d,d,d,d,d,d,d ] + // cd = card detect, cc = card changed, cb = card busy, + // tr = transmitter ready, te = tx fifo empty, + // ra = received byte available, ro = receive overrun, d = received byte + assign spi_rd_data = + { {WIDTH-15{1'b0}}, spi_card_detect, spi_card_changed, spi_card_busy, + spi_tx_ready, spi_tx_empty, + spi_rx_avail, spi_rx_ovr, spi_rx_data }; + + // spi write data: [ 0,...,0,CW,CF,Cx,Cc,Cd,DR,DW,d,d,d,d,d,d,d,d ] + // CW = control write, CF = enable receive filter, Cx = enable transceiver, + // Cc = force spi clock on, Cd = write clock divider, + // DR = read acknowledge, DW = data write, d = byte to be sent + assign spi_ctrl_write = spi_cs && mem_write_enable && mem_write_data[14]; + assign spi_rx_filter_en = mem_write_data[13]; + assign spi_txrx_en = mem_write_data[12]; + assign spi_sclk_f_en = mem_write_data[11]; + assign spi_sclk_div_wr = spi_cs && mem_write_enable && mem_write_data[10]; + assign spi_rx_read = mem_write_data[9]; + assign spi_tx_write = spi_cs && mem_write_enable && mem_write_data[8]; + assign spi_tx_data = mem_write_data[7:0]; + + sdspi sdspi0(.clk(`clock), .reset(~rst), + .tx_data(spi_tx_data), .rx_data(spi_rx_data), + .tx_ready(spi_tx_ready), .tx_empty(spi_tx_empty), + .rx_avail(spi_rx_avail), .rx_ovr(spi_rx_ovr), + .tx_write(spi_tx_write), .rx_read(spi_rx_read), + .card_detect(spi_card_detect), .card_changed(spi_card_changed), .card_busy(spi_card_busy), + // ctrl_write is used with rx_filter_en, txrx_en and spiclk_f_en + .ctrl_write(spi_ctrl_write), + .rx_filter_en(spi_rx_filter_en), .txrx_en(spi_txrx_en), .spiclk_f_en(spi_sclk_f_en), + // + .spiclk_div_wr(spi_sclk_div_wr), + .sd_cs_n(sd_cs_n), + .sd_mosi(sd_mosi), .sd_miso(sd_miso), .sd_sck(sd_sck), .sd_cd(sd_cd)); +`endif + + // UART ----------------------------------------------------------------------- + + // uart write data: [ 0, 0, 0, 0, 0, T, C, 0, c, c, c, c, c, c, c, c ] + // T = transmit enable, C = receiver clear, c = 8-bit-character + // uart read data: [ 0, 0, 0, 0, 0, 0, A, B, c, c, c, c, c, c, c, c ] + // A = char available, B = tx busy, c = 8-bit-character + wire uart_cs = io_enable && (io_slot == 0); + wire uart_tx_en = uart_cs && mem_write_enable && mem_write_data[10]; + wire uart_rx_clear = uart_cs && mem_write_enable && mem_write_data[9]; + wire uart_rx_avail; + wire uart_rx_busy, uart_tx_busy; + wire uart_err; + wire [7:0] uart_rx_data; + wire [7:0] uart_tx_data; + wire [31:0] uart_baud = 32'd115200; + wire [WIDTH-1:0] uart_rd_data; + + assign uart_tx_data = mem_write_data[7:0]; + assign uart_rd_data = { {WIDTH-10{1'b1}}, uart_rx_avail, uart_tx_busy, uart_rx_data }; + + reg timer_tick; + reg[23:0] tick_count; + wire [1:0] irq_in = { timer_tick, uart_rx_avail }; + wire [1:0] irqc_rd_data0; + wire [WIDTH-1:0] irqc_rd_data = { tick_count, 6'b0, irqc_rd_data0 }; + wire irqc_seten = mem_write_data[7]; + wire irqc_cs = io_enable && (io_slot == 3); + + assign io_rd_data = (io_slot == 0) ? uart_rd_data : + `ifdef ENABLE_MICROSD + (io_slot == 1) ? spi_rd_data : + `endif + `ifdef ENABLE_VGAFB + (io_slot == 2) ? fb_rd_data : + `endif + (io_slot == 3) ? irqc_rd_data: + + -1; + + buart #(.CLKFREQ(`clkfreq)) uart0(`clock, rst, + uart_baud, + uart_txd_in, uart_rxd_out, + uart_rx_clear, uart_tx_en, + uart_rx_avail, uart_tx_busy, + uart_tx_data, uart_rx_data); + + // CPU ----------------------------------------------------------------- + stackcpu cpu0(.clk(`clock), .rst(rst), .irq(irq), + .addr(mem_addr), + .data_in(mem_read_data), .read_enable(mem_read_enable), + .data_out(mem_write_data), .write_enable(mem_write_enable), + .mem_wait(mem_wait), + .led1(led1), .led2(led2), .led3(led3), + .debug_out1(debug_data1), + .debug_out2(debug_data2), + .debug_out3(debug_data3), + .debug_out4(debug_data4), + .debug_out5(debug_data5), + .debug_out6(debug_data6)); + + // Interrupt Controller + irqctrl irqctrl0(`clock, irq_in, irqc_cs, mem_write_enable, + irqc_seten, irqc_rd_data0, + irq); + + // count clock ticks + // generate interrupt every 20nth of a second + always @ (posedge `clock) + begin + counter <= counter + 1; + if (counter >= (`clkfreq/20)) + begin + counter <= 0; + timer_tick <= 1; + tick_count <= tick_count + 1'b1; + end + else + begin + timer_tick <= 0; + end + end +endmodule