diff --git a/apple-one.qsf b/apple-one.qsf index 4f0303b..da45d48 100644 --- a/apple-one.qsf +++ b/apple-one.qsf @@ -154,6 +154,7 @@ set_global_assignment -name PRE_FLOW_SCRIPT_FILE "quartus_sh:rtl/build_id.tcl" # end ENTITY(apple1_mist) # ----------------------- +set_global_assignment -name SYSTEMVERILOG_FILE rtl/downloader.sv set_global_assignment -name VERILOG_FILE rtl/display.v set_global_assignment -name VERILOG_FILE rtl/sdram.v set_global_assignment -name VERILOG_FILE "rtl/mist-modules/user_io.v" diff --git a/rtl/apple1_mist.sv b/rtl/apple1_mist.sv index db41a37..3363314 100644 --- a/rtl/apple1_mist.sv +++ b/rtl/apple1_mist.sv @@ -19,6 +19,7 @@ // TODO support ACI interface for load and save // TODO special expansion boards: TMS9918, SID // TODO ascii keyboard +// TODO check diff with updated data_io.v module apple1_mist( input CLOCK_27, @@ -27,9 +28,9 @@ module apple1_mist( input SPI_SCK, output SPI_DO, input SPI_DI, - //input SPI_SS2, + input SPI_SS2, input SPI_SS3, - //input SPI_SS4, + input SPI_SS4, input CONF_DATA0, // SDRAM interface @@ -68,7 +69,9 @@ module apple1_mist( /******************************************************************************************/ localparam CONF_STR = { - "APPLE 1;;", + "APPLE 1;;", // 0 download index for "apple1.rom" + "F,PRG,Load program;", // 1 download index for ".prg" files + "F,ROM,Load firmware;", // 2 download index for ".rom" files "O34,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%;", "T6,Reset;", "V,v1.01.",`BUILD_DATE @@ -95,8 +98,6 @@ wire no_csync; wire ps2_kbd_clk; wire ps2_kbd_data; -assign LED = 1; - wire reset_button = status[0] | st_menu_reset | st_reset_switch | !pll_locked; /******************************************************************************************/ @@ -120,6 +121,48 @@ pll pll */ ); +/******************************************************************************************/ +/******************************************************************************************/ +/***************************************** @downloader ************************************/ +/******************************************************************************************/ +/******************************************************************************************/ + +wire is_downloading; // indicates that downloader is working +wire [24:0] download_addr; // download address +wire [7:0] download_data; // download data +wire download_wr; // download write enable +wire ROM_loaded; // 1 when boot rom has been downloaded + +// ROM download helper +downloader +#( + .BOOT_INDEX (0), // menu index 0 is for automatic download of "apple1.rom" at FPGA boot + .PRG_INDEX (1), // menu index for load .prg + .ROM_INDEX (2), // menu index for load .prg + .ROM_START_ADDR(0) // start of ROM (bank 0 of SDRAM) +) +downloader +( + // new SPI interface + .SPI_DO ( SPI_DO ), + .SPI_DI ( SPI_DI ), + .SPI_SCK( SPI_SCK ), + .SPI_SS2( SPI_SS2 ), + .SPI_SS3( SPI_SS3 ), + .SPI_SS4( SPI_SS4 ), + + // signal indicating an active rom download + .downloading ( is_downloading ), + .ROM_done ( ROM_loaded ), + + // external ram interface + .clk ( clk14 ), + .clk_ena ( 1 ), + .wr ( download_wr ), + .addr ( download_addr ), + .data ( download_data ) +); + /******************************************************************************************/ /******************************************************************************************/ /***************************************** @apple1 ****************************************/ @@ -127,15 +170,50 @@ pll pll /******************************************************************************************/ // RAM -wire [7:0] ram_dout; ram ram( - .clk(clk14), - .address(cpu_addr[12:0]), - .w_en(cpu_wr), - .din(cpu_dout), - .dout(ram_dout) + .clk (clk14 ), + .address(sdram_addr[12:0]), + .w_en (sdram_wr ), + .din (sdram_din ), + .dout (sdram_dout) ); +// SDRAM control signals +//assign SDRAM_CKE = 1'b1; + +wire [24:0] sdram_addr; +wire [7:0] sdram_din; +wire sdram_wr; +wire sdram_rd; +wire [7:0] sdram_dout; + +assign dummy = is_downloading && download_wr; + +always @(*) begin + if(is_downloading && download_wr) begin + sdram_addr <= download_addr; + sdram_din <= download_data; + sdram_wr <= download_wr; + sdram_rd <= 1'b1; + end + /* + else if(eraser_busy) begin + sdram_addr <= eraser_addr; + sdram_din <= eraser_data; + sdram_wr <= eraser_wr; + sdram_rd <= 1'b1; + end + */ + else begin + sdram_addr <= { 12'b0, cpu_addr[12:0] }; + sdram_din <= cpu_dout; + sdram_wr <= cpu_wr; + sdram_rd <= cpu_rd; + end +end + +assign LED = ~dummy; + // WozMon ROM wire [7:0] rom_dout; rom_wozmon rom_wozmon( @@ -164,7 +242,7 @@ wire rom_cs = (cpu_addr[15:8] == 8'b11111111); // 0xFF00 -> 0xFFFF wire [7:0] bus_dout = basic_cs ? basic_dout : rom_cs ? rom_dout : - ram_cs ? ram_dout : + ram_cs ? sdram_dout : 8'b0; diff --git a/rtl/downloader.sv b/rtl/downloader.sv new file mode 100644 index 0000000..3ba24b1 --- /dev/null +++ b/rtl/downloader.sv @@ -0,0 +1,111 @@ +// This module allows easy download of three types of files into system ram: +// +// 1) ROM file that is injected by the firmware at startup/boot (loaded at address 0) +// 2) PRG file, containing load address in the first two bytes as in C64's .prg +// 3) ROM as 1) but with different .rom file name selected by the user from the menu + + +module downloader ( + + // SPI interface with the ARM controller + input SPI_DO, + input SPI_DI, + input SPI_SCK, + input SPI_SS2, + input SPI_SS3, + input SPI_SS4, + + input clk, + input clk_ena, + + output reg wr, + output reg [24:0] addr, + output reg [7:0] data, + + output reg downloading, // 1 = active download is in process + output reg ROM_done // 1 = boot ROM has already been loaded +); + +parameter BOOT_INDEX = 0; // menu index for the boot rom (0 by default in MiST firmware) +parameter PRG_INDEX; // menu index for .prg files +parameter ROM_INDEX; // menu index for .rom files (not loaded at boot) +parameter ROM_START_ADDR = 0; // start of ROM + +reg [15:0] prg_start_addr; // first two bytes of the .prg file containing ram loading address + +// simplify checks +wire is_rom_download = dio_index == BOOT_INDEX || dio_index == ROM_INDEX; +wire is_prg_download = dio_index == PRG_INDEX; + +reg dio_dowloading_old = 0; // used to detect changes in dio_dowloading + +always @(posedge clk) begin + + if(dio_dowloading == 1) begin + downloading <= 1; + + if(is_rom_download) begin + addr <= dio_addr + ROM_START_ADDR; + wr <= dio_wr; + data <= dio_data; + end + else if(is_prg_download) begin + if(dio_addr == 0) begin + prg_start_addr [7:0] = dio_data; // extracts address low byte + wr <= 0; // do not write yet + end + else if(dio_addr == 1) begin + prg_start_addr[15:8] = dio_data; // extracts address high byte + wr <= 0; // do not write yet + end + else begin + addr <= dio_addr + { 9'b0, prg_start_addr } - 2; // write prg at load address and skip first two bytes + wr <= dio_wr; + data <= dio_data; + end + end + end + + // end of transfer + if(dio_dowloading_old == 1 && dio_dowloading == 0) begin + downloading <= 0; + if(is_rom_download) ROM_done <= 1; + end + + // detect change in dio_dowloading + dio_dowloading_old <= dio_dowloading; + +end + +/******************************************************************************************/ +/******************************************************************************************/ +/***************************************** @data_io ***************************************/ +/******************************************************************************************/ +/******************************************************************************************/ + +wire dio_dowloading; +wire [7:0] dio_index; +wire dio_wr; +wire [24:0] dio_addr; +wire [7:0] dio_data; + +data_io data_io ( + .clk_sys ( clk ), + .clkref_n( ~clk_ena ), // keep this to zero + + // io controller spi interface + .SPI_SCK( SPI_SCK ), + .SPI_SS2( SPI_SS2 ), + .SPI_SS4( SPI_SS4 ), + .SPI_DI ( SPI_DI ), + .SPI_DO ( SPI_DO ), + + .ioctl_download ( dio_dowloading ), // signal indicating an active rom download + .ioctl_index ( dio_index ), // 0=rom download, 1=prg dowload + .ioctl_addr ( dio_addr ), + .ioctl_dout ( dio_data ), + .ioctl_wr ( dio_wr ) +); + +endmodule + diff --git a/rtl/mist-modules/_new__data_io.v b/rtl/mist-modules/_new__data_io.v new file mode 100644 index 0000000..7e14f12 --- /dev/null +++ b/rtl/mist-modules/_new__data_io.v @@ -0,0 +1,259 @@ +// +// data_io.v +// +// data_io for the MiST board +// http://code.google.com/p/mist-board/ +// +// Copyright (c) 2014 Till Harbaum +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +/////////////////////////////////////////////////////////////////////// + +module data_io +( + input clk_sys, + input SPI_SCK, + input SPI_SS2, + input SPI_SS4, + input SPI_DI, + inout SPI_DO, + + input clkref_n, // assert ioctl_wr one cycle after clkref stobe (negative active) + + // ARM -> FPGA download + output reg ioctl_download = 0, // signal indicating an active download + output reg ioctl_upload = 0, // signal indicating an active upload + output reg [7:0] ioctl_index, // menu index used to upload the file ([7:6] - extension index, [5:0] - menu index) + // Note: this is also set for user_io mounts. + // Valid when ioctl_download = 1 or when img_mounted strobe is active in user_io. + output reg ioctl_wr, // strobe indicating ioctl_dout valid + output reg [24:0] ioctl_addr, + output reg [7:0] ioctl_dout, + input [7:0] ioctl_din, + output reg [23:0] ioctl_fileext, // file extension + output reg [31:0] ioctl_filesize // file size +); + +parameter START_ADDR = 25'd0; +parameter ROM_DIRECT_UPLOAD = 0; + +/////////////////////////////// DOWNLOADING /////////////////////////////// + +reg [7:0] data_w; +reg [7:0] data_w2 = 0; +reg [3:0] cnt; +reg rclk = 0; +reg rclk2 = 0; +reg addr_reset = 0; +reg downloading_reg = 0; +reg uploading_reg = 0; +reg reg_do; + +localparam DIO_FILE_TX = 8'h53; +localparam DIO_FILE_TX_DAT = 8'h54; +localparam DIO_FILE_INDEX = 8'h55; +localparam DIO_FILE_INFO = 8'h56; +localparam DIO_FILE_RX = 8'h57; +localparam DIO_FILE_RX_DAT = 8'h58; + +assign SPI_DO = reg_do; + +// data_io has its own SPI interface to the io controller +always@(negedge SPI_SCK or posedge SPI_SS2) begin : SPI_TRANSMITTER + reg [7:0] dout_r; + + if(SPI_SS2) begin + reg_do <= 1'bZ; + end else begin + if (cnt == 15) dout_r <= ioctl_din; + reg_do <= dout_r[~cnt[2:0]]; + end +end + + +always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER + reg [6:0] sbuf; + reg [24:0] addr; + reg [7:0] cmd; + reg [5:0] bytecnt; + + if(SPI_SS2) begin + bytecnt <= 0; + cnt <= 0; + end else begin + // don't shift in last bit. It is evaluated directly + // when writing to ram + if(cnt != 15) sbuf <= { sbuf[5:0], SPI_DI}; + + // count 0-7 8-15 8-15 ... + if(cnt != 15) cnt <= cnt + 1'd1; + else cnt <= 8; + + // finished command byte + if(cnt == 7) cmd <= {sbuf, SPI_DI}; + + if(cnt == 15) begin + case (cmd) + // prepare/end transmission + DIO_FILE_TX: begin + // prepare + if(SPI_DI) begin + addr_reset <= ~addr_reset; + downloading_reg <= 1; + end else begin + downloading_reg <= 0; + end + end + + DIO_FILE_RX: begin + // prepare + if(SPI_DI) begin + addr_reset <= ~addr_reset; + uploading_reg <= 1; + end else begin + uploading_reg <= 0; + end + end + + // command 0x57: DIO_FILE_RX_DAT + // command 0x54: DIO_FILE_TX_DAT + DIO_FILE_RX_DAT, + DIO_FILE_TX_DAT: begin + data_w <= {sbuf, SPI_DI}; + rclk <= ~rclk; + end + + // expose file (menu) index + DIO_FILE_INDEX: ioctl_index <= {sbuf, SPI_DI}; + + // receiving FAT directory entry (mist-firmware/fat.h - DIRENTRY) + DIO_FILE_INFO: begin + bytecnt <= bytecnt + 1'd1; + case (bytecnt) + 8'h08: ioctl_fileext[23:16] <= {sbuf, SPI_DI}; + 8'h09: ioctl_fileext[15: 8] <= {sbuf, SPI_DI}; + 8'h0A: ioctl_fileext[ 7: 0] <= {sbuf, SPI_DI}; + 8'h1C: ioctl_filesize[ 7: 0] <= {sbuf, SPI_DI}; + 8'h1D: ioctl_filesize[15: 8] <= {sbuf, SPI_DI}; + 8'h1E: ioctl_filesize[23:16] <= {sbuf, SPI_DI}; + 8'h1F: ioctl_filesize[31:24] <= {sbuf, SPI_DI}; + endcase + end + endcase + end + end +end + +// direct SD Card->FPGA transfer +generate if (ROM_DIRECT_UPLOAD == 1) begin + +always@(posedge SPI_SCK, posedge SPI_SS4) begin : SPI_DIRECT_RECEIVER + reg [6:0] sbuf2; + reg [2:0] cnt2; + reg [9:0] bytecnt; + + if(SPI_SS4) begin + cnt2 <= 0; + bytecnt <= 0; + end else begin + // don't shift in last bit. It is evaluated directly + // when writing to ram + if(cnt2 != 7) + sbuf2 <= { sbuf2[5:0], SPI_DO }; + + cnt2 <= cnt2 + 1'd1; + + // received a byte + if(cnt2 == 7) begin + bytecnt <= bytecnt + 1'd1; + // read 514 byte/sector (512 + 2 CRC) + if (bytecnt == 513) bytecnt <= 0; + // don't send the CRC bytes + if (~bytecnt[9]) begin + data_w2 <= {sbuf2, SPI_DO}; + rclk2 <= ~rclk2; + end + end + end +end + +end +endgenerate + +always@(posedge clk_sys) begin : DATA_OUT + // synchronisers + reg rclkD, rclkD2; + reg rclk2D, rclk2D2; + reg addr_resetD, addr_resetD2; + + reg wr_int, wr_int_direct, rd_int; + reg [24:0] addr; + reg [31:0] filepos; + + // bring flags from spi clock domain into core clock domain + { rclkD, rclkD2 } <= { rclk, rclkD }; + { rclk2D ,rclk2D2 } <= { rclk2, rclk2D }; + { addr_resetD, addr_resetD2 } <= { addr_reset, addr_resetD }; + + ioctl_wr <= 0; + + if (!downloading_reg) begin + ioctl_download <= 0; + wr_int <= 0; + wr_int_direct <= 0; + end + + if (!uploading_reg) begin + ioctl_upload <= 0; + rd_int <= 0; + end + + if (~clkref_n) begin + rd_int <= 0; + wr_int <= 0; + wr_int_direct <= 0; + if (wr_int || wr_int_direct) begin + ioctl_dout <= wr_int ? data_w : data_w2; + ioctl_wr <= 1; + addr <= addr + 1'd1; + ioctl_addr <= addr; + end + if (rd_int) begin + ioctl_addr <= ioctl_addr + 1'd1; + end + end + + // detect transfer start from the SPI receiver + if(addr_resetD ^ addr_resetD2) begin + addr <= START_ADDR; + ioctl_addr <= START_ADDR; + filepos <= 0; + ioctl_download <= downloading_reg; + ioctl_upload <= uploading_reg; + end + + // detect new byte from the SPI receiver + if (rclkD ^ rclkD2) begin + wr_int <= downloading_reg; + rd_int <= uploading_reg; + end + // direct transfer receiver + if (rclk2D ^ rclk2D2 && filepos != ioctl_filesize) begin + filepos <= filepos + 1'd1; + wr_int_direct <= 1; + end +end + +endmodule diff --git a/rtl/mist-modules/data_io.v b/rtl/mist-modules/data_io.v index 7e14f12..8941fe4 100644 --- a/rtl/mist-modules/data_io.v +++ b/rtl/mist-modules/data_io.v @@ -28,20 +28,18 @@ module data_io input SPI_SS2, input SPI_SS4, input SPI_DI, - inout SPI_DO, + input SPI_DO, // yes, SPI_DO is input when SS4 active input clkref_n, // assert ioctl_wr one cycle after clkref stobe (negative active) // ARM -> FPGA download output reg ioctl_download = 0, // signal indicating an active download - output reg ioctl_upload = 0, // signal indicating an active upload output reg [7:0] ioctl_index, // menu index used to upload the file ([7:6] - extension index, [5:0] - menu index) // Note: this is also set for user_io mounts. // Valid when ioctl_download = 1 or when img_mounted strobe is active in user_io. output reg ioctl_wr, // strobe indicating ioctl_dout valid output reg [24:0] ioctl_addr, output reg [7:0] ioctl_dout, - input [7:0] ioctl_din, output reg [23:0] ioctl_fileext, // file extension output reg [31:0] ioctl_filesize // file size ); @@ -53,41 +51,23 @@ parameter ROM_DIRECT_UPLOAD = 0; reg [7:0] data_w; reg [7:0] data_w2 = 0; -reg [3:0] cnt; reg rclk = 0; reg rclk2 = 0; reg addr_reset = 0; reg downloading_reg = 0; -reg uploading_reg = 0; -reg reg_do; localparam DIO_FILE_TX = 8'h53; localparam DIO_FILE_TX_DAT = 8'h54; localparam DIO_FILE_INDEX = 8'h55; localparam DIO_FILE_INFO = 8'h56; -localparam DIO_FILE_RX = 8'h57; -localparam DIO_FILE_RX_DAT = 8'h58; - -assign SPI_DO = reg_do; // data_io has its own SPI interface to the io controller -always@(negedge SPI_SCK or posedge SPI_SS2) begin : SPI_TRANSMITTER - reg [7:0] dout_r; - - if(SPI_SS2) begin - reg_do <= 1'bZ; - end else begin - if (cnt == 15) dout_r <= ioctl_din; - reg_do <= dout_r[~cnt[2:0]]; - end -end - - always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER reg [6:0] sbuf; - reg [24:0] addr; reg [7:0] cmd; + reg [3:0] cnt; reg [5:0] bytecnt; + reg [24:0] addr; if(SPI_SS2) begin bytecnt <= 0; @@ -104,53 +84,37 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER // finished command byte if(cnt == 7) cmd <= {sbuf, SPI_DI}; - if(cnt == 15) begin - case (cmd) - // prepare/end transmission - DIO_FILE_TX: begin - // prepare - if(SPI_DI) begin - addr_reset <= ~addr_reset; - downloading_reg <= 1; - end else begin - downloading_reg <= 0; - end + // prepare/end transmission + if((cmd == DIO_FILE_TX) && (cnt == 15)) begin + // prepare + if(SPI_DI) begin + addr_reset <= ~addr_reset; + downloading_reg <= 1; + end else begin + downloading_reg <= 0; end + end - DIO_FILE_RX: begin - // prepare - if(SPI_DI) begin - addr_reset <= ~addr_reset; - uploading_reg <= 1; - end else begin - uploading_reg <= 0; - end - end + // command 0x54: UIO_FILE_TX + if((cmd == DIO_FILE_TX_DAT) && (cnt == 15)) begin + data_w <= {sbuf, SPI_DI}; + rclk <= ~rclk; + end - // command 0x57: DIO_FILE_RX_DAT - // command 0x54: DIO_FILE_TX_DAT - DIO_FILE_RX_DAT, - DIO_FILE_TX_DAT: begin - data_w <= {sbuf, SPI_DI}; - rclk <= ~rclk; - end + // expose file (menu) index + if((cmd == DIO_FILE_INDEX) && (cnt == 15)) ioctl_index <= {sbuf, SPI_DI}; - // expose file (menu) index - DIO_FILE_INDEX: ioctl_index <= {sbuf, SPI_DI}; - - // receiving FAT directory entry (mist-firmware/fat.h - DIRENTRY) - DIO_FILE_INFO: begin - bytecnt <= bytecnt + 1'd1; - case (bytecnt) - 8'h08: ioctl_fileext[23:16] <= {sbuf, SPI_DI}; - 8'h09: ioctl_fileext[15: 8] <= {sbuf, SPI_DI}; - 8'h0A: ioctl_fileext[ 7: 0] <= {sbuf, SPI_DI}; - 8'h1C: ioctl_filesize[ 7: 0] <= {sbuf, SPI_DI}; - 8'h1D: ioctl_filesize[15: 8] <= {sbuf, SPI_DI}; - 8'h1E: ioctl_filesize[23:16] <= {sbuf, SPI_DI}; - 8'h1F: ioctl_filesize[31:24] <= {sbuf, SPI_DI}; - endcase - end + // receiving FAT directory entry (mist-firmware/fat.h - DIRENTRY) + if((cmd == DIO_FILE_INFO) && (cnt == 15)) begin + bytecnt <= bytecnt + 1'd1; + case (bytecnt) + 8'h08: ioctl_fileext[23:16] <= {sbuf, SPI_DI}; + 8'h09: ioctl_fileext[15: 8] <= {sbuf, SPI_DI}; + 8'h0A: ioctl_fileext[ 7: 0] <= {sbuf, SPI_DI}; + 8'h1C: ioctl_filesize[ 7: 0] <= {sbuf, SPI_DI}; + 8'h1D: ioctl_filesize[15: 8] <= {sbuf, SPI_DI}; + 8'h1E: ioctl_filesize[23:16] <= {sbuf, SPI_DI}; + 8'h1F: ioctl_filesize[31:24] <= {sbuf, SPI_DI}; endcase end end @@ -198,7 +162,7 @@ always@(posedge clk_sys) begin : DATA_OUT reg rclk2D, rclk2D2; reg addr_resetD, addr_resetD2; - reg wr_int, wr_int_direct, rd_int; + reg wr_int, wr_int_direct; reg [24:0] addr; reg [31:0] filepos; @@ -215,13 +179,7 @@ always@(posedge clk_sys) begin : DATA_OUT wr_int_direct <= 0; end - if (!uploading_reg) begin - ioctl_upload <= 0; - rd_int <= 0; - end - if (~clkref_n) begin - rd_int <= 0; wr_int <= 0; wr_int_direct <= 0; if (wr_int || wr_int_direct) begin @@ -230,26 +188,17 @@ always@(posedge clk_sys) begin : DATA_OUT addr <= addr + 1'd1; ioctl_addr <= addr; end - if (rd_int) begin - ioctl_addr <= ioctl_addr + 1'd1; - end end // detect transfer start from the SPI receiver if(addr_resetD ^ addr_resetD2) begin addr <= START_ADDR; - ioctl_addr <= START_ADDR; filepos <= 0; - ioctl_download <= downloading_reg; - ioctl_upload <= uploading_reg; + ioctl_download <= 1; end // detect new byte from the SPI receiver - if (rclkD ^ rclkD2) begin - wr_int <= downloading_reg; - rd_int <= uploading_reg; - end - // direct transfer receiver + if (rclkD ^ rclkD2) wr_int <= 1; if (rclk2D ^ rclk2D2 && filepos != ioctl_filesize) begin filepos <= filepos + 1'd1; wr_int_direct <= 1;