module m6800_interface #( parameter [11:0] HORZ_VISIBLE = 640, parameter [11:0] VERT_VISIBLE = 480 ) ( // System signals input wire i_clk, input wire i_reset, output wire o_led, // Control Register output wire [7:0] o_reg_ctrl, // SDRAM Write Interface output wire o_sdr_wr_request, output wire [15:0] o_sdr_wr_data, output wire [1:0] o_sdr_wr_mask, output wire [21:0] o_sdr_wr_address, input wire i_sdr_wr_done, // 6800 Interface input wire [7:0] io_data8, input wire i_cs, input wire i_dc, input wire i_rw, input wire i_en, output wire o_wait )/* synthesis SYN_NOPRUNE=1 */; localparam LOW = 0, HIGH = 1, FALSE = LOW, TRUE = HIGH, FALSE_n = HIGH, TRUE_n = LOW; // parallel interface wires wire [7:0] rd_io_data = io_data8; wire wr_cmd_6800 = ~i_cs && ~i_dc && ~i_rw; wire wr_data_6800 = ~i_cs && i_dc && ~i_rw; wire rd_data_6800 = ~i_cs && i_dc && i_rw; wire rd_status_6800 = ~i_cs && ~i_dc && i_rw; // ============================================================================= // Interface Command stages // ============================================================================= localparam STAGE_CMD_IDLE = 0, STAGE_SET_COL_ADDR = 1, STAGE_SET_ROW_ADDR = 2, STAGE_READ_RAM = 3, STAGE_WRITE_RAM = 4, STAGE_SET_PAGE = 5, STAGE_DISP_CONTROL = 6, STAGE_SET_FUNCTION = 7; // Stages Reg reg [2:0] cmd_stage = STAGE_CMD_IDLE; reg [11:0] col_start_addr; reg [11:0] col_end_addr; reg [10:0] row_start_addr; reg [10:0] row_end_addr; reg [12:0] start_addr/* synthesis syn_noprune=1 */; reg [3:0] end_addr; reg [11:0] col_addr; reg [10:0] row_addr; reg [20:0] page_start_addr/* synthesis syn_noprune=1 */; reg [7:0] vram_data; reg [15:0] ram_data; reg [7:0] r_control /* synthesis syn_noprune=1 */; reg r_led; // Interface busy LED // ============================================================================= // STROBE 'EN' Edge detector // ============================================================================= reg sync_en,last_en; reg sync_wr_en; reg last_wr_en; reg sig_wr_en; always @(posedge i_clk) if (i_reset) begin sync_en <= FALSE; last_en <= FALSE; sync_wr_en <= FALSE; last_wr_en <= FALSE; end else begin if (~i_cs) begin sync_en <= i_en; last_en <= sync_en; end if (wr_data_6800) begin sync_wr_en <= sig_wr_en; last_wr_en <= sync_wr_en; end end // rising Edge wire en_rising = sync_en && ~last_en; wire fifo_wr_en = sync_wr_en && ~last_wr_en; // falling Edge wire en_falling = ~sync_en && last_en; // ============================================================================= // Data Input Register Sync // ============================================================================= reg [7:0]data_sync /* synthesis syn_noprune=1 */; reg [7:0]cmd_sync /* synthesis syn_noprune=1 */; always @(posedge i_clk) if (i_reset) begin data_sync <= 'd0; cmd_sync <= 'd0; end else if (en_falling && wr_cmd_6800) cmd_sync <= rd_io_data; else if (en_falling && wr_data_6800) data_sync <= rd_io_data; // ============================================================================= // counter // ============================================================================= reg [3:0] counter; always @(posedge i_clk) if (i_reset) counter <= 'd0; else if (en_falling) begin if (wr_cmd_6800) counter <= 'd0; else begin if (cmd_stage == STAGE_WRITE_RAM && counter[0]) counter <= 'd2; else counter <= counter + 'd1; end end // ============================================================================= // Command Idle stage // ============================================================================= localparam SET_COL_ADDR = 8'h15, SET_ROW_ADDR = 8'h75, SET_PAGE_ADDR = 8'h18, WRITE_RAM = 8'h5C, READ_RAM = 8'h5D, DISP_CONTROL = 8'hA0, FUNCTION = 8'hAB; // ============================================================================= // stage always clocked block // ============================================================================= always @(posedge i_clk) if (i_reset) begin cmd_stage <= STAGE_CMD_IDLE; sig_wr_en <= FALSE; col_start_addr <= 'd0; col_end_addr <= HORZ_VISIBLE; row_start_addr <= 'd0; row_end_addr <= VERT_VISIBLE; col_addr <= 'd0; row_addr <= 'd0; r_control <= 8'b0000_0000; end else begin case (cmd_stage) STAGE_CMD_IDLE: begin stage_cmd_idle(); r_led <= LOW; end STAGE_SET_COL_ADDR: begin stage_set_col_addr(); r_led <= HIGH; end STAGE_SET_ROW_ADDR: begin stage_set_row_addr(); r_led <= HIGH; end STAGE_READ_RAM: begin stage_read_ram(); r_led <= HIGH; end STAGE_WRITE_RAM: begin stage_write_ram(); r_led <= HIGH; end STAGE_SET_PAGE: begin stage_set_page(); r_led <= HIGH; end STAGE_DISP_CONTROL: begin stage_disp_control(); r_led <= HIGH; end STAGE_SET_FUNCTION: begin stage_set_function(); r_led <= HIGH; end default: stage_cmd_idle(); endcase end // ============================================================================= // Idle stage task // ============================================================================= task stage_cmd_idle; // Commands sequence if (en_falling && wr_cmd_6800) begin case (rd_io_data) SET_COL_ADDR: cmd_stage <= STAGE_SET_COL_ADDR; SET_ROW_ADDR: cmd_stage <= STAGE_SET_ROW_ADDR; SET_PAGE_ADDR: cmd_stage <= STAGE_SET_PAGE; WRITE_RAM: cmd_stage <= STAGE_WRITE_RAM; READ_RAM: cmd_stage <= STAGE_READ_RAM; DISP_CONTROL: cmd_stage <= STAGE_DISP_CONTROL; FUNCTION: cmd_stage <= STAGE_SET_FUNCTION; default: cmd_stage <= STAGE_CMD_IDLE; endcase end endtask // ============================================================================= // Set Column Start/End Address stage task // ============================================================================= task stage_set_col_addr; begin if (counter == 1) start_addr[11:8] <= data_sync[3:0]; else if (counter == 2) start_addr[7:0] <= data_sync; else if (counter == 3) end_addr[3:0] <= data_sync[3:0]; else if (counter == 4) begin col_start_addr <= start_addr; col_addr <= start_addr; row_addr <= row_start_addr; col_end_addr <= {end_addr[3:0],data_sync}; cmd_stage <= STAGE_CMD_IDLE; end end endtask // ============================================================================= // Set Row Start/End Address stage task // ============================================================================= task stage_set_row_addr; begin if (counter == 1) start_addr[10:8] <= data_sync[2:0]; else if (counter == 2) start_addr[7:0] <= data_sync; else if (counter == 3) end_addr[2:0] <= data_sync[2:0]; else if (counter == 4) begin row_start_addr <= start_addr; col_addr <= col_start_addr; row_addr <= start_addr; row_end_addr <= {end_addr[2:0],data_sync}; cmd_stage <= STAGE_CMD_IDLE; end end endtask // ============================================================================= // Set Page Address stage task // ============================================================================= task stage_set_page; begin if (counter == 1) start_addr[12:8] <= data_sync[4:0]; else if (counter == 2) start_addr[7:0] <= data_sync; else if (counter == 3) begin page_start_addr <= {start_addr[12:0],data_sync}; cmd_stage <= STAGE_CMD_IDLE; end end endtask // ============================================================================= // Read RAM stage task // ============================================================================= task stage_read_ram; begin cmd_stage <= STAGE_CMD_IDLE; end endtask // ============================================================================= // Write RAM stage task // ============================================================================= task stage_write_ram; begin if (counter > 'd0) begin if (counter[0]) begin vram_data[7:0] <= data_sync; sig_wr_en <= FALSE; end else // if (!counter[0]) begin ram_data <= {vram_data[7:0],data_sync}; sig_wr_en <= TRUE; end if (!counter[0] && fifo_wr_en && !r_control[0]) begin if (col_addr < col_end_addr) col_addr <= col_addr +'d1; else begin if (row_addr < row_end_addr) row_addr <= row_addr + 'd1; else row_addr <= row_start_addr; col_addr <= col_start_addr; end end if (!counter[0] && fifo_wr_en && r_control[0]) begin if (row_addr < row_end_addr) row_addr <= row_addr + 'd1; else begin if (col_addr < col_end_addr) col_addr <= col_addr +'d1; else col_addr <= col_start_addr; row_addr <= row_start_addr; end end if (i_rw && i_cs) // counter > 0 && ~wr_data_6800 cmd_stage <= STAGE_CMD_IDLE; end end endtask // ============================================================================= // Display Control Function stage task // ============================================================================= // Display Setup Flags // [A0] = 0 inc. address horizontal before vertical H/V // [A0] = 1 inc. address vertical before horizontal V/H // [A1] = 0 Column Scan Normal // [A1] = 1 Column Scan Invers // [A2] = 0 Row Scan Normal // [A2] = 1 Row Scan Invers // [A5] = 0 Read/Write from/to Page 1 // [A5] = 1 Read/Write from/to Page 2 // [A6] = 0 Display Page 1 // [A6] = 1 Display Page 2 // ============================================================================= task stage_disp_control; begin if (counter == 1) begin r_control[7:0] <= {1'b0,data_sync[6:5],2'b0,data_sync[2:0]}; cmd_stage <= STAGE_CMD_IDLE; end end endtask // ============================================================================= // Display Interface Function stage task // ============================================================================= task stage_set_function; begin cmd_stage <= STAGE_CMD_IDLE; end endtask wire [22:0] wr_row_addr; wire [31:0] pagesize = (HORZ_VISIBLE*(VERT_VISIBLE))/* synthesis syn_noprune=1 */; wire [21:0] wr_addr = r_control[5] ? (col_addr + wr_row_addr[21:0] + pagesize[21:0]) : (col_addr + wr_row_addr[21:0]); multi row( .Clock (i_clk), .ClkEn (1'b1), .Aclr (i_reset), .DataA (HORZ_VISIBLE), .DataB (row_addr), .Result (wr_row_addr) ); wire [15:0] fifo_rd_data; wire [21:0] fifo_rd_address; reg [15:0] sdr_wr_data; reg [21:0] sdr_wr_address; reg [1:0] sdr_wr_mask /* synthesis syn_noprune=1 */; reg fifo_rd_en; wire fifo_empty; reg sdr_wr_request; data_fifo data_fifo( .Clock (i_clk), .Reset (i_reset), .Data (ram_data), .WrEn (fifo_wr_en), .Q (fifo_rd_data), .RdEn (fifo_rd_en), .Empty (fifo_empty), .Full (open), .AlmostFull (o_wait) ); address_fifo addr_fifo( .Clock (i_clk), .Reset (i_reset), .Data (wr_addr), .WrEn (fifo_wr_en), .Q (fifo_rd_address), .RdEn (fifo_rd_en), .Empty (open), .Full (open) ); // ============================================================================= // FIFO RD/WR stages // ============================================================================= localparam STAGE_FIFO_IDLE = 0, STAGE_FIFO_RD_PIPE = 1, STAGE_FIFO_RD_DATA = 2, STAGE_FIFO_WR_SDRAM = 3; reg [1:0] fifo_stage; // ============================================================================= task stage_fifo_idle; begin if (!fifo_empty) begin fifo_rd_en <= TRUE; fifo_stage <= STAGE_FIFO_RD_PIPE; end end endtask // ============================================================================= task stage_fifo_rd_pipe; begin fifo_rd_en <= FALSE; fifo_stage <= STAGE_FIFO_RD_DATA; end endtask // ============================================================================= task stage_fifo_rd_data; begin sdr_wr_mask <= 2'b00; sdr_wr_request <= TRUE; sdr_wr_data <= fifo_rd_data; sdr_wr_address <= fifo_rd_address[21:0]; fifo_stage <= STAGE_FIFO_WR_SDRAM; end endtask // ============================================================================= task stage_fifo_wr_sdram; begin if (i_sdr_wr_done) if (!fifo_empty) begin fifo_rd_en <= TRUE; fifo_stage <= STAGE_FIFO_RD_PIPE; end else fifo_stage <= STAGE_FIFO_IDLE; sdr_wr_request <= FALSE; end endtask // ============================================================================= always @(posedge i_clk) if (i_reset) begin fifo_stage <= STAGE_FIFO_IDLE; fifo_rd_en <= FALSE; sdr_wr_request <= FALSE; sdr_wr_mask <= 2'b11; end else case (fifo_stage) STAGE_FIFO_IDLE: stage_fifo_idle(); STAGE_FIFO_RD_PIPE: stage_fifo_rd_pipe(); STAGE_FIFO_RD_DATA: stage_fifo_rd_data(); STAGE_FIFO_WR_SDRAM: stage_fifo_wr_sdram(); default: stage_cmd_idle(); endcase // sdram interface wires assign o_sdr_wr_request = sdr_wr_request; assign o_sdr_wr_data = sdr_wr_data; assign o_sdr_wr_address = sdr_wr_address; assign o_sdr_wr_mask = sdr_wr_mask; // Control Register assign o_reg_ctrl = r_control; assign o_led = r_led; endmodule