fork Gehstock's project

This commit is contained in:
nino-porcino 2021-12-29 16:18:10 +01:00
parent fe6a59aed6
commit 03281591f6
59 changed files with 25750 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
Test
build_id.v
db
history.db
greybox_tmp
incremental_db
output_files
simulation
PLLJ_PLLSPE_INFO.txt
*.bak
*.orig
*.rej
*.qdf
*.rpt
*.smsg
*.summary
*.done
*.jdi
*.pin
*.sof
*.qws
*.ppf
*.ddb
*.srf

15
README.md Normal file
View File

@ -0,0 +1,15 @@
# Apple1_MIST
Apple1 implementation for the MiST FPGA.
This was forked from [Gehstock's project](https://github.com/Gehstock/Mist_FPGA).
## CHANGELOG
2021-12-28
- 15 kHz video output (NTSC) and use of MiST scandoubler/video pipeline
- more accurate 7x8 character matrix (5x7 + hardware spacing)
- clock is now derived from 14.31818 instead of 25 MHz (more accurate)
- serial port communication feature is disabled/removed

30
apple-one.qpf Normal file
View File

@ -0,0 +1,30 @@
# -------------------------------------------------------------------------- #
#
# Copyright (C) 1991-2013 Altera Corporation
# Your use of Altera Corporation's design tools, logic functions
# and other software and tools, and its AMPP partner logic
# functions, and any output files from any of the foregoing
# (including device programming or simulation files), and any
# associated documentation or information are expressly subject
# to the terms and conditions of the Altera Program License
# Subscription Agreement, Altera MegaCore Function License
# Agreement, or other applicable license agreement, including,
# without limitation, that your use is for the sole purpose of
# programming logic devices manufactured by Altera and sold by
# Altera or its authorized distributors. Please refer to the
# applicable agreement for further details.
#
# -------------------------------------------------------------------------- #
#
# Quartus II 64-Bit
# Version 13.1.0 Build 162 10/23/2013 SJ Web Edition
# Date created = 21:11:27 January 26, 2018
#
# -------------------------------------------------------------------------- #
QUARTUS_VERSION = "13.1"
DATE = "21:11:27 January 26, 2018"
# Revisions
PROJECT_REVISION = "apple-one"

192
apple-one.qsf Normal file
View File

@ -0,0 +1,192 @@
# -------------------------------------------------------------------------- #
#
# Copyright (C) 1991-2013 Altera Corporation
# Your use of Altera Corporation's design tools, logic functions
# and other software and tools, and its AMPP partner logic
# functions, and any output files from any of the foregoing
# (including device programming or simulation files), and any
# associated documentation or information are expressly subject
# to the terms and conditions of the Altera Program License
# Subscription Agreement, Altera MegaCore Function License
# Agreement, or other applicable license agreement, including,
# without limitation, that your use is for the sole purpose of
# programming logic devices manufactured by Altera and sold by
# Altera or its authorized distributors. Please refer to the
# applicable agreement for further details.
#
# -------------------------------------------------------------------------- #
#
# Quartus II 64-Bit
# Version 13.1.0 Build 162 10/23/2013 SJ Web Edition
# Date created = 11:23:36 April 10, 2018
#
# -------------------------------------------------------------------------- #
#
# Notes:
#
# 1) The default values for assignments are stored in the file:
# apple-one_assignment_defaults.qdf
# If this file doesn't exist, see file:
# assignment_defaults.qdf
#
# 2) Altera recommends that you do not modify this file. This
# file is updated automatically by the Quartus II software
# and any changes you make may be lost or overwritten.
#
# -------------------------------------------------------------------------- #
# Project-Wide Assignments
# ========================
set_global_assignment -name ORIGINAL_QUARTUS_VERSION 13.1
set_global_assignment -name PROJECT_CREATION_TIME_DATE "21:11:27 JANUARY 26, 2018"
set_global_assignment -name LAST_QUARTUS_VERSION 13.1
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
# Classic Timing Assignments
# ==========================
set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
# Analysis & Synthesis Assignments
# ================================
set_global_assignment -name FAMILY "Cyclone III"
set_global_assignment -name TOP_LEVEL_ENTITY apple1_mist
set_global_assignment -name DEVICE_FILTER_PIN_COUNT 144
set_global_assignment -name DEVICE_FILTER_SPEED_GRADE 8
# Fitter Assignments
# ==================
set_global_assignment -name DEVICE EP3C25E144C8
set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1
set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V"
set_global_assignment -name CRC_ERROR_OPEN_DRAIN OFF
set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name ENABLE_CONFIGURATION_PINS OFF
set_global_assignment -name ENABLE_NCE_PIN OFF
set_global_assignment -name ENABLE_BOOT_SEL_PIN OFF
set_global_assignment -name CYCLONEIII_CONFIGURATION_SCHEME "PASSIVE SERIAL"
set_global_assignment -name FORCE_CONFIGURATION_VCCIO ON
set_global_assignment -name RESERVE_DATA0_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name RESERVE_DATA1_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name RESERVE_FLASH_NCE_AFTER_CONFIGURATION "USE AS REGULAR IO"
# EDA Netlist Writer Assignments
# ==============================
set_global_assignment -name EDA_SIMULATION_TOOL "ModelSim-Altera (VHDL)"
# Assembler Assignments
# =====================
set_global_assignment -name USE_CONFIGURATION_DEVICE OFF
set_global_assignment -name GENERATE_RBF_FILE ON
# Power Estimation Assignments
# ============================
set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW"
set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)"
# Advanced I/O Timing Assignments
# ===============================
set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -rise
set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -fall
set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -rise
set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -fall
# start EDA_TOOL_SETTINGS(eda_simulation)
# ---------------------------------------
# EDA Netlist Writer Assignments
# ==============================
set_global_assignment -name EDA_OUTPUT_DATA_FORMAT VHDL -section_id eda_simulation
# end EDA_TOOL_SETTINGS(eda_simulation)
# -------------------------------------
# start DESIGN_PARTITION(Top)
# ---------------------------
# Incremental Compilation Assignments
# ===================================
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
# end DESIGN_PARTITION(Top)
# -------------------------
# Pin & Location Assignments
# ==========================
set_location_assignment PIN_7 -to LED
set_location_assignment PIN_54 -to CLOCK_27
set_location_assignment PIN_144 -to VGA_R[5]
set_location_assignment PIN_143 -to VGA_R[4]
set_location_assignment PIN_142 -to VGA_R[3]
set_location_assignment PIN_141 -to VGA_R[2]
set_location_assignment PIN_137 -to VGA_R[1]
set_location_assignment PIN_135 -to VGA_R[0]
set_location_assignment PIN_133 -to VGA_B[5]
set_location_assignment PIN_132 -to VGA_B[4]
set_location_assignment PIN_125 -to VGA_B[3]
set_location_assignment PIN_121 -to VGA_B[2]
set_location_assignment PIN_120 -to VGA_B[1]
set_location_assignment PIN_115 -to VGA_B[0]
set_location_assignment PIN_114 -to VGA_G[5]
set_location_assignment PIN_113 -to VGA_G[4]
set_location_assignment PIN_112 -to VGA_G[3]
set_location_assignment PIN_111 -to VGA_G[2]
set_location_assignment PIN_110 -to VGA_G[1]
set_location_assignment PIN_106 -to VGA_G[0]
set_location_assignment PIN_136 -to VGA_VS
set_location_assignment PIN_119 -to VGA_HS
set_location_assignment PIN_65 -to AUDIO_L
set_location_assignment PIN_80 -to AUDIO_R
set_location_assignment PIN_105 -to SPI_DO
set_location_assignment PIN_88 -to SPI_DI
set_location_assignment PIN_126 -to SPI_SCK
set_location_assignment PIN_127 -to SPI_SS2
set_location_assignment PIN_91 -to SPI_SS3
set_location_assignment PIN_13 -to CONF_DATA0
set_location_assignment PLL_1 -to "pll:pll|altpll:altpll_component"
set_global_assignment -name PRE_FLOW_SCRIPT_FILE "quartus_sh:rtl/build_id.tcl"
# end ENTITY(apple1_mist)
# -----------------------
set_global_assignment -name VERILOG_FILE "rtl/mist-modules/user_io.v"
set_global_assignment -name VERILOG_FILE "rtl/mist-modules/sd_card.v"
set_global_assignment -name VERILOG_FILE "rtl/mist-modules/scandoubler.v"
set_global_assignment -name VERILOG_FILE "rtl/mist-modules/rgb2ypbpr.v"
set_global_assignment -name VERILOG_FILE "rtl/mist-modules/osd.v"
set_global_assignment -name VERILOG_FILE "rtl/mist-modules/mist_video.v"
set_global_assignment -name QIP_FILE "rtl/mist-modules/mist_core.qip"
set_global_assignment -name VHDL_FILE "rtl/mist-modules/mist.vhd"
set_global_assignment -name QIP_FILE "rtl/mist-modules/mist.qip"
set_global_assignment -name VERILOG_FILE "rtl/mist-modules/data_io.v"
set_global_assignment -name VHDL_FILE "rtl/mist-modules/dac.vhd"
set_global_assignment -name SYSTEMVERILOG_FILE "rtl/mist-modules/cofi.sv"
set_global_assignment -name VERILOG_FILE "rtl/mist-modules/arcade_inputs.v"
set_global_assignment -name VERILOG_FILE rtl/arlet_6502/cpu.v
set_global_assignment -name VERILOG_FILE rtl/arlet_6502/ALU.v
set_global_assignment -name VERILOG_FILE rtl/arlet_6502/arlet_6502.v
set_global_assignment -name VERILOG_FILE rtl/apple1.v
set_global_assignment -name VERILOG_FILE rtl/clock.v
set_global_assignment -name VERILOG_FILE rtl/pwr_reset.v
set_global_assignment -name VERILOG_FILE rtl/ram.v
set_global_assignment -name VERILOG_FILE rtl/rom_basic.v
set_global_assignment -name VERILOG_FILE rtl/rom_wozmon.v
set_global_assignment -name VERILOG_FILE rtl/vram.v
set_global_assignment -name VERILOG_FILE rtl/uart.v
set_global_assignment -name VERILOG_FILE rtl/ps2keyboard.v
set_global_assignment -name VERILOG_FILE rtl/vga.v
set_global_assignment -name VERILOG_FILE rtl/async_tx_rx.v
set_global_assignment -name VERILOG_FILE rtl/font_rom.v
set_global_assignment -name SYSTEMVERILOG_FILE rtl/video_mixer.sv
set_global_assignment -name VERILOG_FILE rtl/scandoubler.v
set_global_assignment -name QIP_FILE rtl/pll.qip
set_global_assignment -name VERILOG_FILE rtl/osd.v
set_global_assignment -name VERILOG_FILE rtl/mist_io.v
set_global_assignment -name SYSTEMVERILOG_FILE rtl/hq2x.sv
set_global_assignment -name SYSTEMVERILOG_FILE rtl/apple1_mist.sv
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

37
clean.bat Normal file
View File

@ -0,0 +1,37 @@
@echo off
del /s *.bak
del /s *.orig
del /s *.rej
del /s *~
rmdir /s /q db
rmdir /s /q incremental_db
rmdir /s /q output_files
rmdir /s /q simulation
rmdir /s /q greybox_tmp
rmdir /s /q hc_output
rmdir /s /q .qsys_edit
rmdir /s /q hps_isw_handoff
rmdir /s /q sys\.qsys_edit
rmdir /s /q sys\vip
cd sys
for /d %%i in (*_sim) do rmdir /s /q "%%~nxi"
cd ..
for /d %%i in (*_sim) do rmdir /s /q "%%~nxi"
del build_id.v
del c5_pin_model_dump.txt
del PLLJ_PLLSPE_INFO.txt
del /s *.qws
del /s *.ppf
del /s *.ddb
del /s *.csv
del /s *.cmp
del /s *.sip
del /s *.spd
del /s *.bsf
del /s *.f
del /s *.sopcinfo
del /s *.xml
del /s new_rtl_netlist
del /s old_rtl_netlist
pause

0
pll.qip Normal file
View File

263
rtl/apple1.v Normal file
View File

@ -0,0 +1,263 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: Apple1 hardware core
//
// Author.....: Alan Garfield
// Niels A. Moseley
// Date.......: 26-1-2018
//
module apple1(
input clk14, // 25 MHz master clock
input rst_n, // active low synchronous reset (needed for simulation)
// I/O interface to computer
input uart_rx, // asynchronous serial data input from computer
output uart_tx, // asynchronous serial data output to computer
output uart_cts, // clear to send flag to computer
// I/O interface to keyboard
input ps2_clk, // PS/2 keyboard serial clock input
input ps2_din, // PS/2 keyboard serial data input
input ps2_select, // Input to select the PS/2 keyboard instead of the UART
// Outputs to VGA display
output vga_h_sync, // hozizontal VGA sync pulse
output vga_v_sync, // vertical VGA sync pulse
output vga_red, // red VGA signal
output vga_grn, // green VGA signal
output vga_blu, // blue VGA signal
input vga_cls, // clear screen button
// Debugging ports
output [15:0] pc_monitor // spy for program counter / debugging
);
//////////////////////////////////////////////////////////////////////////
// Registers and Wires
wire [15:0] ab;
wire [7:0] dbi;
wire [7:0] dbo;
wire we;
//////////////////////////////////////////////////////////////////////////
// Clocks
wire cpu_clken;
clock clock(
.clk14(clk14),
.rst_n(rst_n),
.cpu_clken(cpu_clken)
);
//////////////////////////////////////////////////////////////////////////
// Reset
wire rst;
pwr_reset pwr_reset(
.clk14(clk14),
.rst_n(rst_n),
.enable(cpu_clken),
.rst(rst)
);
//////////////////////////////////////////////////////////////////////////
// 6502
arlet_6502 arlet_6502(
.clk (clk14),
.enable (cpu_clken),
.rst (rst),
.ab (ab),
.dbi (dbi),
.dbo (dbo),
.we (we),
.irq_n (1'b1),
.nmi_n (1'b1),
.ready (cpu_clken),
.pc_monitor (pc_monitor)
);
//////////////////////////////////////////////////////////////////////////
// Address Decoding
wire ram_cs = (ab[15:13] == 3'b000); // 0x0000 -> 0x1FFF
// font mode, background and foreground colour
wire vga_mode_cs = (ab[15:2] == 14'b11000000000000); // 0xC000 -> 0xC003
// RX: Either keyboard or UART input
// TX: Always VGA and UART output
wire rx_cs = (ab[15:1] == 15'b110100000001000); // 0xD010 -> 0xD011
wire tx_cs = (ab[15:1] == 15'b110100000001001); // 0xD012 -> 0xD013
// select UART on transmit but only receive when PS/2 is not selected.
wire uart_cs = tx_cs | ((~ps2_select) & rx_cs);
// select PS/2 keyboard input when selected.
wire ps2kb_cs = ps2_select & rx_cs;
// VGA always get characters when they are sent.
wire vga_cs = tx_cs;
wire basic_cs = (ab[15:12] == 4'b1110); // 0xE000 -> 0xEFFF
wire rom_cs = (ab[15:8] == 8'b11111111); // 0xFF00 -> 0xFFFF
//////////////////////////////////////////////////////////////////////////
// RAM and ROM
// RAM
wire [7:0] ram_dout;
ram ram(
.clk(clk14),
.address(ab[12:0]),
.w_en(we & ram_cs),
.din(dbo),
.dout(ram_dout)
);
// WozMon ROM
wire [7:0] rom_dout;
rom_wozmon rom_wozmon(
.clk(clk14),
.address(ab[7:0]),
.dout(rom_dout)
);
// Basic ROM
wire [7:0] basic_dout;
rom_basic rom_basic(
.clk(clk14),
.address(ab[11:0]),
.dout(basic_dout)
);
//////////////////////////////////////////////////////////////////////////
// Peripherals
// UART
wire [7:0] uart_dout;
uart #(
`ifdef SIM
100, 10, 2 // for simulation don't need real baud rates
`else
25000000, 115200, 8 // 25MHz, 115200 baud, 8 times RX oversampling
`endif
) my_uart(
.clk(clk14),
.enable(uart_cs & cpu_clken),
.rst(rst),
.uart_rx(uart_rx),
.uart_tx(uart_tx),
.uart_cts(uart_cts),
.address(ab[1:0]), // for uart
.w_en(we & uart_cs),
.din(dbo),
.dout(uart_dout)
);
// PS/2 keyboard interface
wire [7:0] ps2_dout;
ps2keyboard keyboard(
.clk14(clk14),
.rst(rst),
.key_clk(ps2_clk),
.key_din(ps2_din),
.cs(ps2kb_cs),
.address(ab[0]),
.dout(ps2_dout)
);
// VGA Display interface
reg [2:0] fg_colour;
reg [2:0] bg_colour;
reg [1:0] font_mode;
reg [7:0] vga_mode_dout;
vga vga(
.clk14(clk14),
.enable(vga_cs & cpu_clken),
.rst(rst),
.vga_h_sync(vga_h_sync),
.vga_v_sync(vga_v_sync),
.vga_red(vga_red),
.vga_grn(vga_grn),
.vga_blu(vga_blu),
.address(ab[0]),
.w_en(we & vga_cs),
.din(dbo),
.mode(font_mode),
.fg_colour(fg_colour),
.bg_colour(bg_colour),
.clr_screen(vga_cls)
);
// Handle font mode and foreground and background
// colours. This so isn't Apple One authentic, but
// it can't hurt to have some fun. :D
always @(posedge clk14 or posedge rst)
begin
if (rst)
begin
font_mode <= 2'b0;
fg_colour <= 3'd7;
bg_colour <= 3'd0;
end
else
begin
case (ab[1:0])
2'b00:
begin
vga_mode_dout = {6'b0, font_mode};
if (vga_mode_cs & we & cpu_clken)
font_mode <= dbo[1:0];
end
2'b01:
begin
vga_mode_dout = {5'b0, fg_colour};
if (vga_mode_cs & we & cpu_clken)
fg_colour <= dbo[2:0];
end
2'b10:
begin
vga_mode_dout = {5'b0, bg_colour};
if (vga_mode_cs & we & cpu_clken)
bg_colour <= dbo[2:0];
end
default:
vga_mode_dout = 8'b0;
endcase
end
end
//////////////////////////////////////////////////////////////////////////
// CPU Data In MUX
// link up chip selected device to cpu input
assign dbi = ram_cs ? ram_dout :
rom_cs ? rom_dout :
basic_cs ? basic_dout :
uart_cs ? uart_dout :
ps2kb_cs ? ps2_dout :
vga_mode_cs ? vga_mode_dout :
8'hFF;
endmodule

201
rtl/apple1_mist.sv Normal file
View File

@ -0,0 +1,201 @@
// Apple-1 for MiST
//
// Forked from Gehstock's implementation https://github.com/Gehstock/Mist_FPGA
//
//
// TODO integrate with mist-modules
// TODO load binary files into memory
// TODO support ACI interface for load and save
// TODO additional RAM with SDRAM
// TODO reset key from keyboard
// TODO cls key from keyboard
// TODO power on-off
// TODO special expansion boards: TMS9918, SID
// TODO white/green/amber switch?
// TODO reset if pll not locked
// TODO rename "vga" into "display"
// TODO reorganize file structure
// TODO check ps2 clock
module apple1_mist(
input CLOCK_27,
// SPI interface to arm io controller
input SPI_SCK,
output SPI_DO,
input SPI_DI,
//input SPI_SS2,
input SPI_SS3,
//input SPI_SS4,
input CONF_DATA0,
// SDRAM interface
inout [15:0] SDRAM_DQ, // SDRAM Data bus 16 Bits
output [12:0] SDRAM_A, // SDRAM Address bus 13 Bits
output SDRAM_DQML, // SDRAM Low-byte Data Mask
output SDRAM_DQMH, // SDRAM High-byte Data Mask
output SDRAM_nWE, // SDRAM Write Enable
output SDRAM_nCAS, // SDRAM Column Address Strobe
output SDRAM_nRAS, // SDRAM Row Address Strobe
output SDRAM_nCS, // SDRAM Chip Select
output [1:0] SDRAM_BA, // SDRAM Bank Address
output SDRAM_CLK, // SDRAM Clock
output SDRAM_CKE, // SDRAM Clock Enable
// VGA interface
output [5:0] VGA_R,
output [5:0] VGA_G,
output [5:0] VGA_B,
output VGA_HS,
output VGA_VS,
// other
output LED,
input UART_RX,
output AUDIO_L,
output AUDIO_R
);
`include "rtl\build_id.v"
/******************************************************************************************/
/******************************************************************************************/
/***************************************** @user_io ***************************************/
/******************************************************************************************/
/******************************************************************************************/
localparam CONF_STR = {
"APPLE 1;;",
"O34,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%;",
"T6,Reset;",
"V,v1.00.",`BUILD_DATE
};
localparam conf_str_len = $size(CONF_STR)>>3;
wire st_reset_switch = buttons[1];
wire st_menu_reset = status[6];
wire clk14; // the 14.31818 MHz clock
wire clk_osd; // x4 clock for the OSD menu
wire r, g, b;
wire hs, vs;
wire [31:0] status;
wire [1:0] buttons;
wire [1:0] switches;
wire scandoubler_disable;
wire ypbpr;
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 pll
(
.inclk0(CLOCK_27),
.c0(clk_osd),
.c1(clk14),
.locked(),
.areset()
);
apple1 apple1
(
.clk14(clk14),
.rst_n(~reset_button),
.uart_rx(), // uart not connected
.uart_tx(), // uart not connected
.uart_cts(), // uart not connected
.ps2_clk(ps2_kbd_clk),
.ps2_din(ps2_kbd_data),
.ps2_select(1'b1),
.vga_h_sync(hs),
.vga_v_sync(vs),
.vga_red(r),
.vga_grn(g),
.vga_blu(b),
.vga_cls(), // clear screen button (not connected yet)
.pc_monitor() // debug program counter (not used)
);
mist_video
#(
.COLOR_DEPTH(1),
.OSD_AUTO_CE(1)
)
mist_video
(
.clk_sys(clk_osd), // 2x the VDP clock for the scandoubler
// OSD SPI interface
.SPI_DI(SPI_DI),
.SPI_SCK(SPI_SCK),
.SPI_SS3(SPI_SS3),
.scanlines(2'b00), // scanlines (00-none 01-25% 10-50% 11-75%)
.ce_divider(1), // non-scandoubled pixel clock divider 0 - clk_sys/4, 1 - clk_sys/2
.scandoubler_disable(scandoubler_disable),
.no_csync(no_csync), // 1 = disable csync without scandoubler
.ypbpr(ypbpr), // 1 = YPbPr output on composite sync
.rotate(2'b00), // Rotate OSD [0] - rotate [1] - left or right
.blend(0), // composite-like blending
// video input
.R(r),
.G(g),
.B(b),
.HSync(hs),
.VSync(vs),
// MiST video output signals
.VGA_R(VGA_R),
.VGA_G(VGA_G),
.VGA_B(VGA_B),
.VGA_VS(VGA_VS),
.VGA_HS(VGA_HS),
);
user_io
#(
.STRLEN(conf_str_len)
//.PS2DIV(14) // ps2 clock divider: CLOCK / 14 must be approx = 15 Khz
)
user_io (
.conf_str (CONF_STR ),
.clk_sys (clk14 ),
.SPI_CLK (SPI_SCK ),
.SPI_SS_IO (CONF_DATA0 ),
.SPI_MISO (SPI_DO ),
.SPI_MOSI (SPI_DI ),
.status (status ),
.buttons (buttons ),
.switches (switches ),
.scandoubler_disable ( scandoubler_disable ),
.ypbpr ( ypbpr ),
.no_csync ( no_csync ),
// ps2 interface
.ps2_kbd_clk (ps2_kbd_clk ),
.ps2_kbd_data (ps2_kbd_data )
);
endmodule

108
rtl/arlet_6502/ALU.v Normal file
View File

@ -0,0 +1,108 @@
/*
* ALU.
*
* AI and BI are 8 bit inputs. Result in OUT.
* CI is Carry In.
* CO is Carry Out.
*
* op[3:0] is defined as follows:
*
* 0011 AI + BI
* 0111 AI - BI
* 1011 AI + AI
* 1100 AI | BI
* 1101 AI & BI
* 1110 AI ^ BI
* 1111 AI
*
*/
module ALU( clk, op, right, AI, BI, CI, CO, BCD, OUT, V, Z, N, HC, RDY );
input clk;
input right;
input [3:0] op; // operation
input [7:0] AI;
input [7:0] BI;
input CI;
input BCD; // BCD style carry
output [7:0] OUT;
output CO;
output V;
output Z;
output N;
output HC;
input RDY;
reg [7:0] OUT;
reg CO;
wire V;
wire Z;
reg N;
reg HC;
reg AI7;
reg BI7;
reg [8:0] temp_logic;
reg [7:0] temp_BI;
reg [4:0] temp_l;
reg [4:0] temp_h;
wire [8:0] temp = { temp_h, temp_l[3:0] };
wire adder_CI = (right | (op[3:2] == 2'b11)) ? 0 : CI;
// calculate the logic operations. The 'case' can be done in 1 LUT per
// bit. The 'right' shift is a simple mux that can be implemented by
// F5MUX.
always @* begin
case( op[1:0] )
2'b00: temp_logic = AI | BI;
2'b01: temp_logic = AI & BI;
2'b10: temp_logic = AI ^ BI;
2'b11: temp_logic = AI;
endcase
if( right )
temp_logic = { AI[0], CI, AI[7:1] };
end
// Add logic result to BI input. This only makes sense when logic = AI.
// This stage can be done in 1 LUT per bit, using carry chain logic.
always @* begin
case( op[3:2] )
2'b00: temp_BI = BI; // A+B
2'b01: temp_BI = ~BI; // A-B
2'b10: temp_BI = temp_logic; // A+A
2'b11: temp_BI = 0; // A+0
endcase
end
// HC9 is the half carry bit when doing BCD add
wire HC9 = BCD & (temp_l[3:1] >= 3'd5);
// CO9 is the carry-out bit when doing BCD add
wire CO9 = BCD & (temp_h[3:1] >= 3'd5);
// combined half carry bit
wire temp_HC = temp_l[4] | HC9;
// perform the addition as 2 separate nibble, so we get
// access to the half carry flag
always @* begin
temp_l = temp_logic[3:0] + temp_BI[3:0] + adder_CI;
temp_h = temp_logic[8:4] + temp_BI[7:4] + temp_HC;
end
// calculate the flags
always @(posedge clk)
if( RDY ) begin
AI7 <= AI[7];
BI7 <= temp_BI[7];
OUT <= temp[7:0];
CO <= temp[8] | CO9;
N <= temp[7];
HC <= temp_HC;
end
assign V = AI7 ^ BI7 ^ CO ^ N;
assign Z = ~|OUT;
endmodule

View File

@ -0,0 +1,72 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: A wrapper for Arlet Ottens 6502 CPU core
//
// Author.....: Alan Garfield
// Niels A. Moseley
// Date.......: 26-1-2018
//
module arlet_6502(
input clk, // clock signal
input enable, // clock enable strobe
input rst, // active high reset signal
output reg [15:0] ab, // address bus
input [7:0] dbi, // 8-bit data bus (input)
output reg [7:0] dbo, // 8-bit data bus (output)
output reg we, // active high write enable strobe
input irq_n, // active low interrupt request
input nmi_n, // active low non-maskable interrupt
input ready, // CPU updates when ready = 1
output [15:0] pc_monitor // program counter monitor signal for debugging
);
wire [7:0] dbo_c;
wire [15:0] ab_c;
wire we_c;
cpu arlet_cpu(
.clk(clk),
.reset(rst),
.AB(ab_c),
.DI(dbi),
.DO(dbo_c),
.WE(we_c),
.IRQ(~irq_n),
.NMI(~nmi_n),
.RDY(ready),
.PC_MONITOR(pc_monitor)
);
always @(posedge clk or posedge rst)
begin
if (rst)
begin
ab <= 16'd0;
dbo <= 8'd0;
we <= 1'b0;
end
else
if (enable)
begin
ab <= ab_c;
dbo <= dbo_c;
we <= we_c;
end
end
endmodule

View File

@ -0,0 +1,66 @@
`include "../rtl/cpu/aholme/chip_6502_nodes.inc"
module LOGIC (
input [`NUM_NODES-1:0] i,
output [`NUM_NODES-1:0] o);
`include "chip_6502_logic.inc"
endmodule
module chip_6502 (
input clk, // FPGA clock
input phi, // 6502 clock
input res,
input so,
input rdy,
input nmi,
input irq,
input [7:0] dbi,
output [7:0] dbo,
output rw,
output sync,
output [15:0] ab);
// Node states
wire [`NUM_NODES-1:0] no;
reg [`NUM_NODES-1:0] ni;
reg [`NUM_NODES-1:0] q = 0;
LOGIC logic_00 (.i(ni), .o(no));
always @ (posedge clk)
q <= no;
always @* begin
ni = q;
ni[`NODE_vcc ] = 1'b1;
ni[`NODE_vss ] = 1'b0;
ni[`NODE_res ] = res;
ni[`NODE_clk0] = phi;
ni[`NODE_so ] = so;
ni[`NODE_rdy ] = rdy;
ni[`NODE_nmi ] = nmi;
ni[`NODE_irq ] = irq;
{ni[`NODE_db7],ni[`NODE_db6],ni[`NODE_db5],ni[`NODE_db4],
ni[`NODE_db3],ni[`NODE_db2],ni[`NODE_db1],ni[`NODE_db0]} = dbi[7:0];
end
assign dbo[7:0] = {
no[`NODE_db7],no[`NODE_db6],no[`NODE_db5],no[`NODE_db4],
no[`NODE_db3],no[`NODE_db2],no[`NODE_db1],no[`NODE_db0]
};
assign ab[15:0] = {
no[`NODE_ab15], no[`NODE_ab14], no[`NODE_ab13], no[`NODE_ab12],
no[`NODE_ab11], no[`NODE_ab10], no[`NODE_ab9], no[`NODE_ab8],
no[`NODE_ab7], no[`NODE_ab6], no[`NODE_ab5], no[`NODE_ab4],
no[`NODE_ab3], no[`NODE_ab2], no[`NODE_ab1], no[`NODE_ab0]
};
assign rw = no[`NODE_rw];
assign sync = no[`NODE_sync];
endmodule

View File

@ -0,0 +1,10 @@
module MUX #(
parameter N=1
) (
output wire o,
input wire i,
input wire [N-1:0] s,
input wire [N-1:0] d);
assign o = (|s) ? &(d|(~s)) : i;
endmodule

1244
rtl/arlet_6502/cpu.v Normal file

File diff suppressed because it is too large Load Diff

227
rtl/async_tx_rx.v Normal file
View File

@ -0,0 +1,227 @@
////////////////////////////////////////////////////////
// RS-232 RX and TX module
// (c) fpga4fun.com & KNJN LLC - 2003 to 2016
// The RS-232 settings are fixed
// TX: 8-bit data, 2 stop, no-parity
// RX: 8-bit data, 1 stop, no-parity (the receiver can accept more stop bits of course)
////////////////////////////////////////////////////////
module async_transmitter(
input clk,
input rst,
input TxD_start,
input [7:0] TxD_data,
output TxD,
output TxD_busy
);
// Assert TxD_start for (at least) one clock cycle to start transmission of TxD_data
// TxD_data is latched so that it doesn't have to stay valid while it is being sent
parameter ClkFrequency = 25000000; // 25MHz
parameter Baud = 115200;
////////////////////////////////
wire BitTick;
BaudTickGen #(ClkFrequency, Baud) tickgen(.clk(clk), .rst(rst), .enable(TxD_busy), .tick(BitTick));
reg [3:0] TxD_state;
reg [7:0] TxD_shift;
wire TxD_ready = (TxD_state==0);
assign TxD_busy = ~TxD_ready;
always @(posedge clk or posedge rst)
begin
if (rst)
begin
TxD_state <= 0;
TxD_shift <= 0;
end
else
begin
if(TxD_ready & TxD_start)
TxD_shift <= TxD_data;
else
if(TxD_state[3] & BitTick)
TxD_shift <= (TxD_shift >> 1);
case(TxD_state)
4'b0000: if(TxD_start) TxD_state <= 4'b0100;
4'b0100: if(BitTick) TxD_state <= 4'b1000; // start bit
4'b1000: if(BitTick) TxD_state <= 4'b1001; // bit 0
4'b1001: if(BitTick) TxD_state <= 4'b1010; // bit 1
4'b1010: if(BitTick) TxD_state <= 4'b1011; // bit 2
4'b1011: if(BitTick) TxD_state <= 4'b1100; // bit 3
4'b1100: if(BitTick) TxD_state <= 4'b1101; // bit 4
4'b1101: if(BitTick) TxD_state <= 4'b1110; // bit 5
4'b1110: if(BitTick) TxD_state <= 4'b1111; // bit 6
4'b1111: if(BitTick) TxD_state <= 4'b0010; // bit 7
4'b0010: if(BitTick) TxD_state <= 4'b0011; // stop1
4'b0011: if(BitTick) TxD_state <= 4'b0000; // stop2
default: if(BitTick) TxD_state <= 4'b0000;
endcase
end
end
assign TxD = (TxD_state<4) | (TxD_state[3] & TxD_shift[0]);
endmodule
////////////////////////////////////////////////////////
module async_receiver(
input clk,
input rst,
input RxD,
output reg RxD_data_ready,
output reg [7:0] RxD_data, // data received, valid only (for one clock cycle) when RxD_data_ready is asserted
// We also detect if a gap occurs in the received stream of characters
// That can be useful if multiple characters are sent in burst
// so that multiple characters can be treated as a "packet"
output RxD_idle, // asserted when no data has been received for a while
output reg RxD_endofpacket = 0 // asserted for one clock cycle when a packet has been detected (i.e. RxD_idle is going high)
);
parameter ClkFrequency = 25000000; // 12MHz
parameter Baud = 115200;
parameter Oversampling = 8; // needs to be a power of 2
// we oversample the RxD line at a fixed rate to capture each RxD data bit at the "right" time
// 8 times oversampling by default, use 16 for higher quality reception
////////////////////////////////
reg [3:0] RxD_state;
wire OversamplingTick;
BaudTickGen #(ClkFrequency, Baud, Oversampling) tickgen(.clk(clk), .rst(rst), .enable(1'b1), .tick(OversamplingTick));
// synchronize RxD to our clk domain
reg [1:0] RxD_sync; // 2'b11
always @(posedge clk or posedge rst)
begin
if (rst)
RxD_sync <= 2'b11;
else
if(OversamplingTick) RxD_sync <= {RxD_sync[0], RxD};
end
// and filter it
reg [1:0] Filter_cnt; // 2'b11
reg RxD_bit; // 1'b1
always @(posedge clk or posedge rst)
begin
if (rst)
begin
Filter_cnt <= 2'b11;
RxD_bit <= 1'b1;
end
else
if(OversamplingTick)
begin
if(RxD_sync[1]==1'b1 && Filter_cnt!=2'b11) Filter_cnt <= Filter_cnt + 1'd1;
else if(RxD_sync[1]==1'b0 && Filter_cnt!=2'b00) Filter_cnt <= Filter_cnt - 1'd1;
if(Filter_cnt==2'b11) RxD_bit <= 1'b1;
else if(Filter_cnt==2'b00) RxD_bit <= 1'b0;
end
end
// and decide when is the good time to sample the RxD line
function integer log2(input integer v);
begin
log2=0;
while(v>>log2)
log2 = log2 + 1;
end
endfunction
localparam l2o = log2(Oversampling);
reg [l2o-2:0] OversamplingCnt;
always @(posedge clk)
if(OversamplingTick) OversamplingCnt <= (RxD_state==0) ? 1'd0 : OversamplingCnt + 1'd1;
wire sampleNow = OversamplingTick && (OversamplingCnt==Oversampling/2-1);
// now we can accumulate the RxD bits in a shift-register
always @(posedge clk or posedge rst)
begin
if (rst)
RxD_state <= 0;
else
case(RxD_state)
4'b0000: if(~RxD_bit) RxD_state <= 4'b0001; // start bit found?
4'b0001: if(sampleNow) RxD_state <= 4'b1000; // sync start bit to sampleNow
4'b1000: if(sampleNow) RxD_state <= 4'b1001; // bit 0
4'b1001: if(sampleNow) RxD_state <= 4'b1010; // bit 1
4'b1010: if(sampleNow) RxD_state <= 4'b1011; // bit 2
4'b1011: if(sampleNow) RxD_state <= 4'b1100; // bit 3
4'b1100: if(sampleNow) RxD_state <= 4'b1101; // bit 4
4'b1101: if(sampleNow) RxD_state <= 4'b1110; // bit 5
4'b1110: if(sampleNow) RxD_state <= 4'b1111; // bit 6
4'b1111: if(sampleNow) RxD_state <= 4'b0010; // bit 7
4'b0010: if(sampleNow) RxD_state <= 4'b0000; // stop bit
default: RxD_state <= 4'b0000;
endcase
end
always @(posedge clk or posedge rst)
begin
if (rst)
RxD_data <= 0;
else
if (sampleNow && RxD_state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]};
end
always @(posedge clk or posedge rst)
begin
if (rst)
RxD_data_ready <= 0;
else
RxD_data_ready <= (sampleNow && RxD_state==4'b0010 && RxD_bit); // make sure a stop bit is received
end
reg [l2o+1:0] GapCnt;
always @(posedge clk or posedge rst)
begin
if (rst)
GapCnt <= 0;
else
if (RxD_state!=0) GapCnt<=0; else if(OversamplingTick & ~GapCnt[log2(Oversampling)+1]) GapCnt <= GapCnt + 1'h1;
end
assign RxD_idle = GapCnt[l2o+1];
always @(posedge clk)
RxD_endofpacket <= OversamplingTick & ~GapCnt[l2o+1] & &GapCnt[l2o:0];
endmodule
////////////////////////////////////////////////////////
module BaudTickGen(
input clk, rst, enable,
output tick // generate a tick at the specified baud rate * oversampling
);
parameter ClkFrequency = 25000000;
parameter Baud = 115200;
parameter Oversampling = 1;
function integer log2(input integer v); begin log2=0; while(v>>log2) log2=log2+1; end endfunction
localparam AccWidth = log2(ClkFrequency/Baud)+8; // +/- 2% max timing error over a byte
reg [AccWidth:0] Acc;
localparam ShiftLimiter = log2(Baud*Oversampling >> (31-AccWidth)); // this makes sure Inc calculation doesn't overflow
localparam Inc = ((Baud*Oversampling << (AccWidth-ShiftLimiter))+(ClkFrequency>>(ShiftLimiter+1)))/(ClkFrequency>>ShiftLimiter);
always @(posedge clk)
begin
if (rst)
Acc <= 0;
else
if(enable) Acc <= Acc[AccWidth-1:0] + Inc[AccWidth:0]; else Acc <= Inc[AccWidth:0];
end
assign tick = Acc[AccWidth];
endmodule

35
rtl/build_id.tcl Normal file
View File

@ -0,0 +1,35 @@
# ================================================================================
#
# Build ID Verilog Module Script
# Jeff Wiencrot - 8/1/2011
#
# Generates a Verilog module that contains a timestamp,
# from the current build. These values are available from the build_date, build_time,
# physical_address, and host_name output ports of the build_id module in the build_id.v
# Verilog source file.
#
# ================================================================================
proc generateBuildID_Verilog {} {
# Get the timestamp (see: http://www.altera.com/support/examples/tcl/tcl-date-time-stamp.html)
set buildDate [ clock format [ clock seconds ] -format %y%m%d ]
set buildTime [ clock format [ clock seconds ] -format %H%M%S ]
# Create a Verilog file for output
set outputFileName "rtl/build_id.v"
set outputFile [open $outputFileName "w"]
# Output the Verilog source
puts $outputFile "`define BUILD_DATE \"$buildDate\""
puts $outputFile "`define BUILD_TIME \"$buildTime\""
close $outputFile
# Send confirmation message to the Messages window
post_message "Generated build identification Verilog module: [pwd]/$outputFileName"
post_message "Date: $buildDate"
post_message "Time: $buildTime"
}
# Comment out this line to prevent the process from automatically executing when the file is sourced:
generateBuildID_Verilog

55
rtl/clock.v Normal file
View File

@ -0,0 +1,55 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: Clock divider to provide clock enables for
// devices.
//
// Author.....: Alan Garfield
// Niels A. Moseley
// Date.......: 29-1-2018
//
module clock
(
input clk14, // 14MHz clock master clock
input rst_n, // active low synchronous reset
// Clock enables
output reg cpu_clken // 1MHz clock enable for the CPU and devices
);
// generate clock enable once every
// 14 clocks. This will (hopefully) make
// the 6502 run at 1 MHz
//
// the clock division counter is synchronously
// reset using rst_n to avoid undefined signals
// in simulation
//
reg [4:0] clk_div;
always @(posedge clk14)
begin
if ((clk_div == 14) || (rst_n == 1'b0))
clk_div <= 0;
else
clk_div <= clk_div + 1'b1;
cpu_clken <= (clk_div[4:0] == 0);
end
endmodule

72
rtl/debounce.v Normal file
View File

@ -0,0 +1,72 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: PS/2 keyboard debounce logic to be used for the
// clock line
//
// Author.....: Niels A. Moseley
// Date.......: 8-2-2018
//
module debounce(
input clk14, // 25MHz clock
input rst, // active high reset
input sig_in, // input signal
output reg sig_out // debounced output signal
);
wire clk_enb; // enable triggering at clk14 divided by 64
reg [5:0] clk_div; // clock divider counter
reg sig_ff1; // first input signal synchronizer
reg sig_ff2; // second input signal synchronizer
assign clk_enb = (clk_div == 6'd0);
// clock divider
always @(posedge clk14 or posedge rst)
begin
if (rst)
clk_div <= 6'd0;
else
clk_div <= clk_div + 6'd1;
end
// debounce timer
always @(posedge clk14 or posedge rst)
begin
if (rst)
begin
sig_out <= 1'b0;
sig_ff1 <= 1'b0;
sig_ff2 <= 1'b0;
end
else if (clk_enb)
begin
// this runs ar approximately 391k Hz
// giving a debounce time of around 2.5us
sig_ff1 <= sig_in;
sig_ff2 <= sig_ff1;
if ((sig_ff1 ^ sig_ff2) == 1'd0)
begin
sig_out <= sig_ff2;
end
end
end
endmodule

70
rtl/font_rom.v Normal file
View File

@ -0,0 +1,70 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: 8KB RAM for system
//
// Author.....: Alan Garfield
// Date.......: 3-2-2018
//
module font_rom (
input clk, // clock signal
input [1:0] mode, // character mode
input [5:0] character, // address bus
input [3:0] pixel, // address of the pixel to output
input [4:0] line, // address of the line to output
output reg out // single pixel from address and pixel pos
);
reg [7:0] rom[0:1023];
initial
$readmemh("roms/vga_font_bitreversed.hex", rom, 0, 1023);
// double height of pixel by ignoring bit 0
wire [3:0] line_ptr = line[4:1];
// Note: Quartus II reverses the pixels when we do:
//
// rom[address][bitindex]
//
// directly, so we use an intermediate
// signal, romout, to work around this
// problem.
//
// IceCube2 and Yosys don't seem to have this problem.
//
reg [7:0] romout;
always @(posedge clk)
begin
// mode
// 00 - normal
// 01 - vertical scanlines
// 10 - horizontal scanlines
// 11 - dotty mode
romout = rom[(character * 10) + {2'd0, line_ptr}];
out <= (mode[1] & line[0]) ? 1'b0 :
(mode[0] & pixel[0]) ? 1'b0 :
romout[pixel[3:1]];
end
endmodule

167
rtl/led_and_key.v Normal file
View File

@ -0,0 +1,167 @@
module ledAndKey(
input clk,
input clk_en,
input rst,
input [3:0] display,
input [7:0] digit1,
input [7:0] digit2,
input [7:0] digit3,
input [7:0] digit4,
input [7:0] digit5,
input [7:0] digit6,
input [7:0] digit7,
input [7:0] digit8,
input [7:0] leds,
output reg [7:0] keys,
output reg tm_cs,
output tm_clk,
inout tm_dio
);
localparam
HIGH = 1'b1,
LOW = 1'b0;
localparam [7:0]
C_READ = 8'b01000010,
C_WRITE = 8'b01000000,
C_DISP = 8'b10001111,
C_ADDR = 8'b11000000;
reg counter;
reg [5:0] instruction_step;
// set up tristate IO pin for display
// tm_dio is physical pin
// dio_in for reading from display
// dio_out for sending to display
// tm_rw selects input or output
reg tm_rw;
wire dio_in, dio_out;
SB_IO #(
.PIN_TYPE(6'b101001),
.PULLUP(1'b1)
) tm_dio_io (
.PACKAGE_PIN(tm_dio),
.OUTPUT_ENABLE(tm_rw),
.D_IN_0(dio_in),
.D_OUT_0(dio_out)
);
// setup tm1638 module with it's tristate IO
// tm_in is read from module
// tm_out is written to module
// tm_latch triggers the module to read/write display
// tm_rw selects read or write mode to display
// busy indicates when module is busy
// (another latch will interrupt)
// tm_clk is the data clk
// dio_in for reading from display
// dio_out for sending to display
//
// tm_data the tristate io pin to module
reg tm_latch;
wire busy;
wire [7:0] tm_data, tm_in;
reg [7:0] tm_out;
assign tm_in = tm_data;
assign tm_data = tm_rw ? tm_out : 8'hZZ;
tm1638 u_tm1638 (
.clk(clk),
.clk_en(clk_en),
.rst(rst),
.data_latch(tm_latch),
.data(tm_data),
.rw(tm_rw),
.busy(busy),
.sclk(tm_clk),
.dio_in(dio_in),
.dio_out(dio_out)
);
always @(posedge clk) begin
if (clk_en) begin
if (rst) begin
instruction_step <= 6'b0;
tm_cs <= HIGH;
tm_rw <= HIGH;
counter <= 1'b0;
keys <= 8'b0;
end else begin
if (counter && ~busy) begin
case (instruction_step)
// *** KEYS ***
1: {tm_cs, tm_rw} <= {LOW, HIGH};
2: {tm_latch, tm_out} <= {HIGH, C_READ}; // read mode
3: {tm_latch, tm_rw} <= {HIGH, LOW};
// read back keys S1 - S8
4: {keys[7], keys[3]} <= {tm_in[0], tm_in[4]};
5: {tm_latch} <= {HIGH};
6: {keys[6], keys[2]} <= {tm_in[0], tm_in[4]};
7: {tm_latch} <= {HIGH};
8: {keys[5], keys[1]} <= {tm_in[0], tm_in[4]};
9: {tm_latch} <= {HIGH};
10: {keys[4], keys[0]} <= {tm_in[0], tm_in[4]};
11: {tm_cs} <= {HIGH};
// *** DISPLAY ***
12: {tm_cs, tm_rw} <= {LOW, HIGH};
13: {tm_latch, tm_out} <= {HIGH, C_WRITE}; // write mode
14: {tm_cs} <= {HIGH};
15: {tm_cs, tm_rw} <= {LOW, HIGH};
16: {tm_latch, tm_out} <= {HIGH, C_ADDR}; // set addr 0 pos
17: {tm_latch, tm_out} <= {HIGH, digit1}; // Digit 1
18: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[7]}}; // LED 1
19: {tm_latch, tm_out} <= {HIGH, digit2}; // Digit 2
20: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[6]}}; // LED 2
21: {tm_latch, tm_out} <= {HIGH, digit3}; // Digit 3
22: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[5]}}; // LED 3
23: {tm_latch, tm_out} <= {HIGH, digit4}; // Digit 4
24: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[4]}}; // LED 4
25: {tm_latch, tm_out} <= {HIGH, digit5}; // Digit 5
26: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[3]}}; // LED 5
27: {tm_latch, tm_out} <= {HIGH, digit6}; // Digit 6
28: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[2]}}; // LED 6
29: {tm_latch, tm_out} <= {HIGH, digit7}; // Digit 7
30: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[1]}}; // LED 7
31: {tm_latch, tm_out} <= {HIGH, digit8}; // Digit 8
32: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[0]}}; // LED 8
33: {tm_cs} <= {HIGH};
34: {tm_cs, tm_rw} <= {LOW, HIGH};
35: {tm_latch, tm_out} <= {HIGH, {4'b1000, display}}; // display
36: {tm_cs, instruction_step} <= {HIGH, 6'b0};
endcase
instruction_step <= instruction_step + 1;
end else if (busy) begin
// pull latch low next clock cycle after module has been
// latched
tm_latch <= LOW;
end
counter <= ~counter;
end
end
end
endmodule

1
rtl/mist-modules/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.bak

View File

@ -0,0 +1,33 @@
Common components for MiST board
================================
This repository contains common components, which should be used by almost all cores.
The modules:
- user_io.v - communicating with the IO controller.
- data_io.v - handling file uploads from the IO controller.
- mist_video.v - a video pipeline, which gives an optional scandoubler, OSD and rgb2ypbpr conversion.
- osd.v, scandoubler.v, rgb2ypbpr.sv, cofi.sv - these are used in mist_video, but can be used separately, too.
- sd_card.v - gives an SPI interface with SD-Card commands towards the IO-Controller, accessing .VHD and other mounted files.
- dac.vhd - a simple sigma-delta DAC for audio output.
- arcade_inputs.v - mostly for arcade-style games, gives access to the joysticks with MAME-style keyboard mapping.
- mist.vhd - VHDL component declarations for user_io and mist_video.
- mist_core.qip - collects the core components, which are needed in almost every case.
Usage hints
===========
All of these components should be clocked by a synchronous clock to the core. The data between the external SPI
interface and this internal clock are synchronized. However to make Quartus' job easier, you have to tell it to
don't try to optimize paths between the SPI and the system clock domain. Also you have to define the incoming
27 MHz and the SPI clocks. These lines in the .sdc file do that:
```
set sys_clk "your_system_clock"
create_clock -name {clk_27} -period 37.037 -waveform { 0.000 18.500 } [get_ports {CLOCK_27[0]}]
create_clock -name {SPI_SCK} -period 41.666 -waveform { 20.8 41.666 } [get_ports {SPI_SCK}]
set_clock_groups -asynchronous -group [get_clocks {SPI_SCK}] -group [get_clocks $sys_clk]
```
Replace "your_system_clock" with the name of the pll clock, like "pll|altpll_component|auto_generated|pll1|clk[0]".

View File

@ -0,0 +1,191 @@
/* Provides arcade controls from joystick/keyboard
Keyboard has a simplified (ESC-coin, F1-F4 start) and MAME-style mapping */
module arcade_inputs(
// clock, same as for userio
input clk,
// signals from userio
input key_strobe,
input key_pressed,
input [7:0] key_code,
input [19:0] joystick_0,
input [19:0] joystick_1,
input [19:0] joystick_2,
input [19:0] joystick_3,
// required rotating of controls
input rotate,
// original orientation [1]-left/right if portrait, [0]-landscape/portrait
input [1:0] orientation,
// joystick_0 and joystick_1 should be swapped
input joyswap,
// player1 and player2 should get both joystick_0 and joystick_1
input oneplayer,
// tilt, coin4-1, start4-1
output [8:0] controls,
// up2, down2, left2, right2, fire12-1, up, down, left, right
output [19:0] player1,
output [19:0] player2,
output [19:0] player3,
output [19:0] player4
);
assign controls = { btn_tilt,
btn_coin | btn_coin4_mame, btn_coin | btn_coin3_mame, btn_coin | btn_coin2_mame, btn_coin | btn_coin1_mame,
btn_four_players | btn_start4_mame, btn_three_players | btn_start3_mame, btn_two_players | btn_start2_mame, btn_one_player | btn_start1_mame };
wire [19:0] joy0 = joyswap ? joystick_1 : joystick_0;
wire [19:0] joy1 = joyswap ? joystick_0 : joystick_1;
wire [19:0] joy2 = joystick_2;
wire [19:0] joy3 = joystick_3;
wire [19:0] p1;
wire [19:0] p2;
wire [19:0] p3;
wire [19:0] p4;
assign p1[15:4] = joy0[15:4] | { 4'h0, btn_fireH, btn_fireG, btn_fireF, btn_fireE, btn_fireD, btn_fireC, btn_fireB, btn_fireA };
assign p2[15:4] = joy1[15:4] | { 4'h0, btn_fire2H, btn_fire2G, btn_fire2F, btn_fire2E, btn_fire2D, btn_fire2C, btn_fire2B, btn_fire2A };
assign p3[15:4] = joy2[15:4];
assign p4[15:4] = joy3[15:4];
// Left or only stick
control_rotator l1(joy0[3:0], {btn_up, btn_down, btn_left, btn_right }, rotate, orientation, p1[3:0]);
control_rotator l2(joy1[3:0], {btn_up2, btn_down2, btn_left2, btn_right2}, rotate, orientation, p2[3:0]);
control_rotator l3(joy2[3:0], 4'd0, rotate, orientation, p3[3:0]);
control_rotator l4(joy3[3:0], 4'd0, rotate, orientation, p4[3:0]);
// Right stick
control_rotator r1(joy0[19:16], 4'd0, rotate, orientation, p1[19:16]);
control_rotator r2(joy1[19:16], 4'd0, rotate, orientation, p2[19:16]);
control_rotator r3(joy2[19:16], 4'd0, rotate, orientation, p3[19:16]);
control_rotator r4(joy3[19:16], 4'd0, rotate, orientation, p4[19:16]);
assign player1 = oneplayer ? p1 | p2 : p1;
assign player2 = oneplayer ? p1 | p2 : p2;
assign player3 = p3;
assign player4 = p4;
// keyboard controls
reg btn_tilt = 0;
reg btn_one_player = 0;
reg btn_two_players = 0;
reg btn_three_players = 0;
reg btn_four_players = 0;
reg btn_left = 0;
reg btn_right = 0;
reg btn_down = 0;
reg btn_up = 0;
reg btn_fireA = 0;
reg btn_fireB = 0;
reg btn_fireC = 0;
reg btn_fireD = 0;
reg btn_fireE = 0;
reg btn_fireF = 0;
reg btn_fireG = 0;
reg btn_fireH = 0;
reg btn_coin = 0;
reg btn_start1_mame = 0;
reg btn_start2_mame = 0;
reg btn_start3_mame = 0;
reg btn_start4_mame = 0;
reg btn_coin1_mame = 0;
reg btn_coin2_mame = 0;
reg btn_coin3_mame = 0;
reg btn_coin4_mame = 0;
reg btn_up2 = 0;
reg btn_down2 = 0;
reg btn_left2 = 0;
reg btn_right2 = 0;
reg btn_fire2A = 0;
reg btn_fire2B = 0;
reg btn_fire2C = 0;
reg btn_fire2D = 0;
reg btn_fire2E = 0;
reg btn_fire2F = 0;
reg btn_fire2G = 0;
reg btn_fire2H = 0;
always @(posedge clk) begin
if(key_strobe) begin
case(key_code)
'h75: btn_up <= key_pressed; // up
'h72: btn_down <= key_pressed; // down
'h6B: btn_left <= key_pressed; // left
'h74: btn_right <= key_pressed; // right
'h76: btn_coin <= key_pressed; // ESC
'h05: btn_one_player <= key_pressed; // F1
'h06: btn_two_players <= key_pressed; // F2
'h04: btn_three_players <= key_pressed; // F3
'h0C: btn_four_players <= key_pressed; // F4
'h14: btn_fireA <= key_pressed; // ctrl
'h11: btn_fireB <= key_pressed; // alt
'h29: btn_fireC <= key_pressed; // Space
'h12: btn_fireD <= key_pressed; // l-shift
'h1A: btn_fireE <= key_pressed; // Z
'h22: btn_fireF <= key_pressed; // X
'h21: btn_fireG <= key_pressed; // C
'h2A: btn_fireH <= key_pressed; // V
'h66: btn_tilt <= key_pressed; // Backspace
// JPAC/IPAC/MAME Style Codes
'h16: btn_start1_mame <= key_pressed; // 1
'h1E: btn_start2_mame <= key_pressed; // 2
'h26: btn_start3_mame <= key_pressed; // 3
'h25: btn_start4_mame <= key_pressed; // 4
'h2E: btn_coin1_mame <= key_pressed; // 5
'h36: btn_coin2_mame <= key_pressed; // 6
'h3D: btn_coin3_mame <= key_pressed; // 7
'h3E: btn_coin4_mame <= key_pressed; // 8
'h2D: btn_up2 <= key_pressed; // R
'h2B: btn_down2 <= key_pressed; // F
'h23: btn_left2 <= key_pressed; // D
'h34: btn_right2 <= key_pressed; // G
'h1C: btn_fire2A <= key_pressed; // A
'h1B: btn_fire2B <= key_pressed; // S
'h15: btn_fire2C <= key_pressed; // Q
'h1D: btn_fire2D <= key_pressed; // W
'h43: btn_fire2E <= key_pressed; // I
'h42: btn_fire2F <= key_pressed; // K
'h3B: btn_fire2G <= key_pressed; // J
'h4B: btn_fire2H <= key_pressed; // L
endcase
end
end
endmodule
module control_rotator (
input [3:0] joystick, //UDLR
input [3:0] keyboard,
input rotate,
input [1:0] orientation,
output [3:0] out
);
assign out = { m_up, m_down, m_left, m_right };
wire m_up = ~(orientation[0] ^ rotate) ? keyboard[3] | joystick[3] : ((orientation[1] ^ orientation[0]) ? keyboard[0] | joystick[0] : keyboard[1] | joystick[1]);
wire m_down = ~(orientation[0] ^ rotate) ? keyboard[2] | joystick[2] : ((orientation[1] ^ orientation[0]) ? keyboard[1] | joystick[1] : keyboard[0] | joystick[0]);
wire m_left = ~(orientation[0] ^ rotate) ? keyboard[1] | joystick[1] : ((orientation[1] ^ orientation[0]) ? keyboard[3] | joystick[3] : keyboard[2] | joystick[2]);
wire m_right = ~(orientation[0] ^ rotate) ? keyboard[0] | joystick[0] : ((orientation[1] ^ orientation[0]) ? keyboard[2] | joystick[2] : keyboard[3] | joystick[3]);
endmodule
// A simple toggle-switch
module input_toggle(
input clk,
input reset,
input btn,
output reg state
);
reg btn_old;
always @(posedge clk) begin
btn_old <= btn;
if (reset) state <= 0;
else if (~btn_old & btn) state <= ~state;
end
endmodule

59
rtl/mist-modules/cofi.sv Normal file
View File

@ -0,0 +1,59 @@
// Composite-like horizontal blending by Kitrinx
// AMR - disable shift register recognition
(* altera_attribute = "-name AUTO_SHIFT_REGISTER_RECOGNITION OFF" *)
module cofi (
input clk,
input pix_ce,
input enable,
input hblank,
input vblank,
input hs,
input vs,
input [VIDEO_DEPTH-1:0] red,
input [VIDEO_DEPTH-1:0] green,
input [VIDEO_DEPTH-1:0] blue,
output reg hblank_out,
output reg vblank_out,
output reg hs_out,
output reg vs_out,
output reg [VIDEO_DEPTH-1:0] red_out,
output reg [VIDEO_DEPTH-1:0] green_out,
output reg [VIDEO_DEPTH-1:0] blue_out
);
parameter VIDEO_DEPTH=8;
function bit [VIDEO_DEPTH-1:0] color_blend (
input [VIDEO_DEPTH-1:0] color_prev,
input [VIDEO_DEPTH-1:0] color_curr,
input blank_last
);
begin
color_blend = blank_last ? color_curr : (color_prev >> 1) + (color_curr >> 1);
end
endfunction
reg [VIDEO_DEPTH-1:0] red_last;
reg [VIDEO_DEPTH-1:0] green_last;
reg [VIDEO_DEPTH-1:0] blue_last;
wire ce = enable ? pix_ce : 1'b1;
always @(posedge clk) if (ce) begin
hblank_out <= hblank;
vblank_out <= vblank;
vs_out <= vs;
hs_out <= hs;
red_last <= red;
blue_last <= blue;
green_last <= green;
red_out <= enable ? color_blend(red_last, red, hblank_out) : red;
blue_out <= enable ? color_blend(blue_last, blue, hblank_out) : blue;
green_out <= enable ? color_blend(green_last, green, hblank_out) : green;
end
endmodule

48
rtl/mist-modules/dac.vhd Normal file
View File

@ -0,0 +1,48 @@
-------------------------------------------------------------------------------
--
-- Delta-Sigma DAC
--
-- Refer to Xilinx Application Note XAPP154.
--
-- This DAC requires an external RC low-pass filter:
--
-- dac_o 0---XXXXX---+---0 analog audio
-- 3k3 |
-- === 4n7
-- |
-- GND
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity dac is
generic (
C_bits : integer := 8
);
port (
clk_i : in std_logic;
res_n_i : in std_logic;
dac_i : in std_logic_vector(C_bits-1 downto 0);
dac_o : out std_logic
);
end dac;
architecture rtl of dac is
signal sig_in: unsigned(C_bits downto 0);
begin
seq: process(clk_i, res_n_i)
begin
if res_n_i = '0' then
sig_in <= to_unsigned(2**C_bits, sig_in'length);
dac_o <= '0';
elsif rising_edge(clk_i) then
-- not dac_i(C_bits-1) effectively adds 0x8..0 to dac_i
--sig_in <= sig_in + unsigned(sig_in(C_bits) & (not dac_i(C_bits-1)) & dac_i(C_bits-2 downto 0));
sig_in <= sig_in + unsigned(sig_in(C_bits) & dac_i);
dac_o <= sig_in(C_bits);
end if;
end process seq;
end rtl;

259
rtl/mist-modules/data_io.v Normal file
View File

@ -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 <till@harbaum.org>
//
// 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 <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////
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

10
rtl/mist-modules/mist.qip Normal file
View File

@ -0,0 +1,10 @@
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) mist.vhd]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) user_io.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) data_io.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) mist_video.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) osd.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) arcade_inputs.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) rgb2ypbpr.v]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) cofi.sv]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) dac.vhd]

123
rtl/mist-modules/mist.vhd Normal file
View File

@ -0,0 +1,123 @@
-- user_io
-- Interface to the MiST IO Controller
-- mist_video
-- A video pipeline for MiST. Just insert between the core video output and the VGA pins
-- Provides an optional scandoubler, a rotateable OSD and (optional) RGb->YPbPr conversion
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
package mist is
component user_io
generic(
STRLEN : integer := 0;
PS2DIV : integer := 100;
ROM_DIRECT_UPLOAD : boolean := false;
SD_IMAGES: integer := 2;
PS2BIDIR : boolean := false;
FEATURES: std_logic_vector(31 downto 0) := (others=>'0')
);
port (
clk_sys : in std_logic;
clk_sd : in std_logic := '0';
SPI_CLK, SPI_SS_IO, SPI_MOSI :in std_logic;
SPI_MISO : out std_logic;
conf_str : in std_logic_vector(8*STRLEN-1 downto 0) := (others => '0');
conf_addr : out std_logic_vector(9 downto 0);
conf_chr : in std_logic_vector(7 downto 0) := (others => '0');
joystick_0 : out std_logic_vector(31 downto 0);
joystick_1 : out std_logic_vector(31 downto 0);
joystick_2 : out std_logic_vector(31 downto 0);
joystick_3 : out std_logic_vector(31 downto 0);
joystick_4 : out std_logic_vector(31 downto 0);
joystick_analog_0 : out std_logic_vector(31 downto 0);
joystick_analog_1 : out std_logic_vector(31 downto 0);
status : out std_logic_vector(63 downto 0);
switches : out std_logic_vector(1 downto 0);
buttons : out std_logic_vector(1 downto 0);
scandoubler_disable : out std_logic;
ypbpr : out std_logic;
no_csync : out std_logic;
core_mod : out std_logic_vector(6 downto 0);
sd_lba : in std_logic_vector(31 downto 0) := (others => '0');
sd_rd : in std_logic_vector(SD_IMAGES-1 downto 0) := (others => '0');
sd_wr : in std_logic_vector(SD_IMAGES-1 downto 0) := (others => '0');
sd_ack : out std_logic;
sd_ack_conf : out std_logic;
sd_conf : in std_logic := '0';
sd_sdhc : in std_logic := '1';
img_size : out std_logic_vector(63 downto 0);
img_mounted : out std_logic_vector(SD_IMAGES-1 downto 0);
sd_buff_addr : out std_logic_vector(8 downto 0);
sd_dout : out std_logic_vector(7 downto 0);
sd_din : in std_logic_vector(7 downto 0) := (others => '0');
sd_dout_strobe : out std_logic;
sd_din_strobe : out std_logic;
ps2_kbd_clk : out std_logic;
ps2_kbd_data : out std_logic;
ps2_kbd_clk_i : in std_logic := '1';
ps2_kbd_data_i : in std_logic := '1';
key_pressed : out std_logic;
key_extended : out std_logic;
key_code : out std_logic_vector(7 downto 0);
key_strobe : out std_logic;
ps2_mouse_clk : out std_logic;
ps2_mouse_data : out std_logic;
ps2_mouse_clk_i : in std_logic := '1';
ps2_mouse_data_i : in std_logic := '1';
mouse_x : out signed(8 downto 0);
mouse_y : out signed(8 downto 0);
mouse_z : out signed(3 downto 0);
mouse_flags : out std_logic_vector(7 downto 0); -- YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn
mouse_strobe : out std_logic;
mouse_idx : out std_logic
);
end component user_io;
component mist_video
generic (
OSD_COLOR : std_logic_vector(2 downto 0) := "110";
OSD_X_OFFSET : std_logic_vector(9 downto 0) := (others => '0');
OSD_Y_OFFSET : std_logic_vector(9 downto 0) := (others => '0');
SD_HCNT_WIDTH: integer := 9;
COLOR_DEPTH : integer := 6;
OSD_AUTO_CE : boolean := true;
SYNC_AND : boolean := false
);
port (
clk_sys : in std_logic;
SPI_SCK : in std_logic;
SPI_SS3 : in std_logic;
SPI_DI : in std_logic;
scanlines : in std_logic_vector(1 downto 0);
ce_divider : in std_logic := '0';
scandoubler_disable : in std_logic;
ypbpr : in std_logic;
rotate : in std_logic_vector(1 downto 0);
no_csync : in std_logic := '0';
blend : in std_logic := '0';
HSync : in std_logic;
VSync : in std_logic;
R : in std_logic_vector(COLOR_DEPTH-1 downto 0);
G : in std_logic_vector(COLOR_DEPTH-1 downto 0);
B : in std_logic_vector(COLOR_DEPTH-1 downto 0);
VGA_HS : out std_logic;
VGA_VS : out std_logic;
VGA_R : out std_logic_vector(5 downto 0);
VGA_G : out std_logic_vector(5 downto 0);
VGA_B : out std_logic_vector(5 downto 0)
);
end component mist_video;
end package;

View File

@ -0,0 +1,7 @@
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) mist.vhd]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) user_io.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) mist_video.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) osd.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) rgb2ypbpr.v]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) cofi.sv]

View File

@ -0,0 +1,156 @@
// A video pipeline for MiST. Just insert between the core video output and the VGA pins
// Provides an optional scandoubler, a rotateable OSD and (optional) RGb->YPbPr conversion
module mist_video
(
// master clock
// it should be 4x (or 2x) pixel clock for the scandoubler
input clk_sys,
// OSD SPI interface
input SPI_SCK,
input SPI_SS3,
input SPI_DI,
// scanlines (00-none 01-25% 10-50% 11-75%)
input [1:0] scanlines,
// non-scandoubled pixel clock divider 0 - clk_sys/4, 1 - clk_sys/2
input ce_divider,
// 0 = HVSync 31KHz, 1 = CSync 15KHz
input scandoubler_disable,
// disable csync without scandoubler
input no_csync,
// YPbPr always uses composite sync
input ypbpr,
// Rotate OSD [0] - rotate [1] - left or right
input [1:0] rotate,
// composite-like blending
input blend,
// video in
input [COLOR_DEPTH-1:0] R,
input [COLOR_DEPTH-1:0] G,
input [COLOR_DEPTH-1:0] B,
input HSync,
input VSync,
// MiST video output signals
output reg [5:0] VGA_R,
output reg [5:0] VGA_G,
output reg [5:0] VGA_B,
output reg VGA_VS,
output reg VGA_HS
);
parameter OSD_COLOR = 3'd4;
parameter OSD_X_OFFSET = 10'd0;
parameter OSD_Y_OFFSET = 10'd0;
parameter SD_HCNT_WIDTH = 9;
parameter COLOR_DEPTH = 6; // 1-6
parameter OSD_AUTO_CE = 1'b1;
parameter SYNC_AND = 1'b0; // 0 - XOR, 1 - AND
wire [5:0] SD_R_O;
wire [5:0] SD_G_O;
wire [5:0] SD_B_O;
wire SD_HS_O;
wire SD_VS_O;
wire pixel_ena;
scandoubler #(SD_HCNT_WIDTH, COLOR_DEPTH) scandoubler
(
.clk_sys ( clk_sys ),
.bypass ( scandoubler_disable ),
.ce_divider ( ce_divider ),
.scanlines ( scanlines ),
.pixel_ena ( pixel_ena ),
.hs_in ( HSync ),
.vs_in ( VSync ),
.r_in ( R ),
.g_in ( G ),
.b_in ( B ),
.hs_out ( SD_HS_O ),
.vs_out ( SD_VS_O ),
.r_out ( SD_R_O ),
.g_out ( SD_G_O ),
.b_out ( SD_B_O )
);
wire [5:0] osd_r_o;
wire [5:0] osd_g_o;
wire [5:0] osd_b_o;
osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR, OSD_AUTO_CE) osd
(
.clk_sys ( clk_sys ),
.rotate ( rotate ),
.ce ( pixel_ena ),
.SPI_DI ( SPI_DI ),
.SPI_SCK ( SPI_SCK ),
.SPI_SS3 ( SPI_SS3 ),
.R_in ( SD_R_O ),
.G_in ( SD_G_O ),
.B_in ( SD_B_O ),
.HSync ( SD_HS_O ),
.VSync ( SD_VS_O ),
.R_out ( osd_r_o ),
.G_out ( osd_g_o ),
.B_out ( osd_b_o )
);
wire [5:0] cofi_r, cofi_g, cofi_b;
wire cofi_hs, cofi_vs;
cofi #(6) cofi (
.clk ( clk_sys ),
.pix_ce ( pixel_ena ),
.enable ( blend ),
.hblank ( ~SD_HS_O ),
.hs ( SD_HS_O ),
.vs ( SD_VS_O ),
.red ( osd_r_o ),
.green ( osd_g_o ),
.blue ( osd_b_o ),
.hs_out ( cofi_hs ),
.vs_out ( cofi_vs ),
.red_out ( cofi_r ),
.green_out( cofi_g ),
.blue_out( cofi_b )
);
wire hs, vs, cs;
wire [5:0] r,g,b;
RGBtoYPbPr #(6) rgb2ypbpr
(
.clk ( clk_sys ),
.ena ( ypbpr ),
.red_in ( cofi_r ),
.green_in ( cofi_g ),
.blue_in ( cofi_b ),
.hs_in ( cofi_hs ),
.vs_in ( cofi_vs ),
.cs_in ( SYNC_AND ? (cofi_hs & cofi_vs) : ~(cofi_hs ^ cofi_vs) ),
.red_out ( r ),
.green_out ( g ),
.blue_out ( b ),
.hs_out ( hs ),
.vs_out ( vs ),
.cs_out ( cs )
);
always @(posedge clk_sys) begin
VGA_R <= r;
VGA_G <= g;
VGA_B <= b;
// a minimig vga->scart cable expects a composite sync signal on the VGA_HS output.
// and VCC on VGA_VS (to switch into rgb mode)
VGA_HS <= ((~no_csync & scandoubler_disable) || ypbpr)? cs : hs;
VGA_VS <= ((~no_csync & scandoubler_disable) || ypbpr)? 1'b1 : vs;
end
endmodule

216
rtl/mist-modules/osd.v Normal file
View File

@ -0,0 +1,216 @@
// A simple OSD implementation. Can be hooked up between a cores
// VGA output and the physical VGA pins
module osd (
// OSDs pixel clock, should be synchronous to cores pixel clock to
// avoid jitter.
input clk_sys,
input ce,
// SPI interface
input SPI_SCK,
input SPI_SS3,
input SPI_DI,
input [1:0] rotate, //[0] - rotate [1] - left or right
// VGA signals coming from core
input [5:0] R_in,
input [5:0] G_in,
input [5:0] B_in,
input HSync,
input VSync,
// VGA signals going to video connector
output [5:0] R_out,
output [5:0] G_out,
output [5:0] B_out
);
parameter OSD_X_OFFSET = 11'd0;
parameter OSD_Y_OFFSET = 11'd0;
parameter OSD_COLOR = 3'd0;
parameter OSD_AUTO_CE = 1'b1;
localparam OSD_WIDTH = 11'd256;
localparam OSD_HEIGHT = 11'd128;
localparam OSD_WIDTH_PADDED = OSD_WIDTH + (OSD_WIDTH >> 1); // 25% padding left and right
// *********************************************************************************
// spi client
// *********************************************************************************
// this core supports only the display related OSD commands
// of the minimig
reg osd_enable;
(* ramstyle = "no_rw_check" *) reg [7:0] osd_buffer[2047:0]; // the OSD buffer itself
// the OSD has its own SPI interface to the io controller
always@(posedge SPI_SCK, posedge SPI_SS3) begin
reg [4:0] cnt;
reg [10:0] bcnt;
reg [7:0] sbuf;
reg [7:0] cmd;
if(SPI_SS3) begin
cnt <= 0;
bcnt <= 0;
end else begin
sbuf <= {sbuf[6:0], SPI_DI};
// 0:7 is command, rest payload
if(cnt < 15) cnt <= cnt + 1'd1;
else cnt <= 8;
if(cnt == 7) begin
cmd <= {sbuf[6:0], SPI_DI};
// lower three command bits are line address
bcnt <= {sbuf[1:0], SPI_DI, 8'h00};
// command 0x40: OSDCMDENABLE, OSDCMDDISABLE
if(sbuf[6:3] == 4'b0100) osd_enable <= SPI_DI;
end
// command 0x20: OSDCMDWRITE
if((cmd[7:3] == 5'b00100) && (cnt == 15)) begin
osd_buffer[bcnt] <= {sbuf[6:0], SPI_DI};
bcnt <= bcnt + 1'd1;
end
end
end
// *********************************************************************************
// video timing and sync polarity anaylsis
// *********************************************************************************
// horizontal counter
reg [10:0] h_cnt;
reg [10:0] hs_low, hs_high;
wire hs_pol = hs_high < hs_low;
wire [10:0] dsp_width = hs_pol ? hs_low : hs_high;
// vertical counter
reg [10:0] v_cnt;
reg [10:0] vs_low, vs_high;
wire vs_pol = vs_high < vs_low;
wire [10:0] dsp_height = vs_pol ? vs_low : vs_high;
wire doublescan = (dsp_height>350);
reg auto_ce_pix;
always @(posedge clk_sys) begin
reg [15:0] cnt = 0;
reg [2:0] pixsz;
reg [2:0] pixcnt;
reg hs;
cnt <= cnt + 1'd1;
hs <= HSync;
pixcnt <= pixcnt + 1'd1;
if(pixcnt == pixsz) pixcnt <= 0;
auto_ce_pix <= !pixcnt;
if(hs && ~HSync) begin
cnt <= 0;
if(cnt <= OSD_WIDTH_PADDED * 2) pixsz <= 0;
else if(cnt <= OSD_WIDTH_PADDED * 3) pixsz <= 1;
else if(cnt <= OSD_WIDTH_PADDED * 4) pixsz <= 2;
else if(cnt <= OSD_WIDTH_PADDED * 5) pixsz <= 3;
else if(cnt <= OSD_WIDTH_PADDED * 6) pixsz <= 4;
else pixsz <= 5;
pixcnt <= 0;
auto_ce_pix <= 1;
end
end
wire ce_pix = OSD_AUTO_CE ? auto_ce_pix : ce;
always @(posedge clk_sys) begin
reg hsD;
reg vsD;
if(ce_pix) begin
// bring hsync into local clock domain
hsD <= HSync;
// falling edge of HSync
if(!HSync && hsD) begin
h_cnt <= 0;
hs_high <= h_cnt;
end
// rising edge of HSync
else if(HSync && !hsD) begin
h_cnt <= 0;
hs_low <= h_cnt;
v_cnt <= v_cnt + 1'd1;
end else begin
h_cnt <= h_cnt + 1'd1;
end
vsD <= VSync;
// falling edge of VSync
if(!VSync && vsD) begin
v_cnt <= 0;
// if the difference is only one line, that might be interlaced picture
if (vs_high != v_cnt + 1'd1) vs_high <= v_cnt;
end
// rising edge of VSync
else if(VSync && !vsD) begin
v_cnt <= 0;
// if the difference is only one line, that might be interlaced picture
if (vs_low != v_cnt + 1'd1) vs_low <= v_cnt;
end
end
end
// area in which OSD is being displayed
reg [10:0] h_osd_start;
reg [10:0] h_osd_end;
reg [10:0] v_osd_start;
reg [10:0] v_osd_end;
always @(posedge clk_sys) begin
h_osd_start <= ((dsp_width - OSD_WIDTH)>> 1) + OSD_X_OFFSET;
h_osd_end <= h_osd_start + OSD_WIDTH;
v_osd_start <= ((dsp_height- (OSD_HEIGHT<<doublescan))>> 1) + OSD_Y_OFFSET;
v_osd_end <= v_osd_start + (OSD_HEIGHT<<doublescan);
end
wire [10:0] osd_hcnt = h_cnt - h_osd_start;
wire [10:0] osd_vcnt = v_cnt - v_osd_start;
wire [10:0] osd_hcnt_next = osd_hcnt + 2'd1; // one pixel offset for osd pixel
wire [10:0] osd_hcnt_next2 = osd_hcnt + 2'd2; // two pixel offset for osd byte address register
reg osd_de;
reg [10:0] osd_buffer_addr;
wire [7:0] osd_byte = osd_buffer[osd_buffer_addr];
reg osd_pixel;
always @(posedge clk_sys) begin
if(ce_pix) begin
osd_buffer_addr <= rotate[0] ? {rotate[1] ? osd_hcnt_next2[7:5] : ~osd_hcnt_next2[7:5],
rotate[1] ? (doublescan ? ~osd_vcnt[7:0] : ~{osd_vcnt[6:0], 1'b0}) :
(doublescan ? osd_vcnt[7:0] : {osd_vcnt[6:0], 1'b0})} :
{doublescan ? osd_vcnt[7:5] : osd_vcnt[6:4], osd_hcnt_next2[7:0]};
osd_pixel <= rotate[0] ? osd_byte[rotate[1] ? osd_hcnt_next[4:2] : ~osd_hcnt_next[4:2]] :
osd_byte[doublescan ? osd_vcnt[4:2] : osd_vcnt[3:1]];
osd_de <= osd_enable &&
(HSync != hs_pol) && ((h_cnt + 1'd1) >= h_osd_start) && ((h_cnt + 1'd1) < h_osd_end) &&
(VSync != vs_pol) && (v_cnt >= v_osd_start) && (v_cnt < v_osd_end);
end
end
assign R_out = !osd_de ? R_in : {osd_pixel, osd_pixel, OSD_COLOR[2], R_in[5:3]};
assign G_out = !osd_de ? G_in : {osd_pixel, osd_pixel, OSD_COLOR[1], G_in[5:3]};
assign B_out = !osd_de ? B_in : {osd_pixel, osd_pixel, OSD_COLOR[0], B_in[5:3]};
endmodule

0
rtl/mist-modules/pll.qip Normal file
View File

View File

@ -0,0 +1,103 @@
// Multiplier-based RGB -> YPbPr conversion
// Copyright 2020/2021 by Alastair M. Robinson
module RGBtoYPbPr
(
input clk,
input ena,
input [WIDTH-1:0] red_in,
input [WIDTH-1:0] green_in,
input [WIDTH-1:0] blue_in,
input hs_in,
input vs_in,
input cs_in,
input pixel_in,
output [WIDTH-1:0] red_out,
output [WIDTH-1:0] green_out,
output [WIDTH-1:0] blue_out,
output reg hs_out,
output reg vs_out,
output reg cs_out,
output reg pixel_out
);
parameter WIDTH = 8;
reg [8+WIDTH-1:0] r_y;
reg [8+WIDTH-1:0] g_y;
reg [8+WIDTH-1:0] b_y;
reg [8+WIDTH-1:0] r_b;
reg [8+WIDTH-1:0] g_b;
reg [8+WIDTH-1:0] b_b;
reg [8+WIDTH-1:0] r_r;
reg [8+WIDTH-1:0] g_r;
reg [8+WIDTH-1:0] b_r;
reg [8+WIDTH-1:0] y;
reg [8+WIDTH-1:0] b;
reg [8+WIDTH-1:0] r;
reg hs_d;
reg vs_d;
reg cs_d;
reg pixel_d;
assign red_out = r[8+WIDTH-1:8];
assign green_out = y[8+WIDTH-1:8];
assign blue_out = b[8+WIDTH-1:8];
// Multiply in the first stage...
always @(posedge clk) begin
hs_d <= hs_in; // Register sync, pixel clock, etc
vs_d <= vs_in; // so they're delayed the same amount as the incoming video
cs_d <= cs_in;
pixel_d <= pixel_in;
if(ena) begin
// (Y = 0.299*R + 0.587*G + 0.114*B)
r_y <= red_in * 8'd76;
g_y <= green_in * 8'd150;
b_y <= blue_in * 8'd29;
// (Pb = -0.169*R - 0.331*G + 0.500*B)
r_b <= red_in * 8'd43;
g_b <= green_in * 8'd84;
b_b <= blue_in * 8'd128;
// (Pr = 0.500*R - 0.419*G - 0.081*B)
r_r <= red_in * 8'd128;
g_r <= green_in * 8'd107;
b_r <= blue_in * 8'd20;
end else begin
r_r[8+WIDTH-1:8] <= red_in; // Passthrough
g_y[8+WIDTH-1:8] <= green_in;
b_b[8+WIDTH-1:8] <= blue_in;
end
end
// Second stage - adding
always @(posedge clk) begin
hs_out <= hs_d;
vs_out <= vs_d;
cs_out <= cs_d;
pixel_out <= pixel_d;
if(ena) begin
y <= r_y + g_y + b_y;
b <= 2'd2**(8+WIDTH-1) + b_b - r_b - g_b;
r <= 2'd2**(8+WIDTH-1) + r_r - g_r - b_r;
end else begin
y <= g_y; // Passthrough
b <= b_b;
r <= r_r;
end
end
endmodule

View File

@ -0,0 +1,229 @@
//
// scandoubler.v
//
// Copyright (c) 2015 Till Harbaum <till@harbaum.org>
//
// 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 <http://www.gnu.org/licenses/>.
// TODO: Delay vsync one line
module scandoubler
(
// system interface
input clk_sys,
input bypass,
input ce_divider,
output pixel_ena,
// scanlines (00-none 01-25% 10-50% 11-75%)
input [1:0] scanlines,
// shifter video interface
input hs_in,
input vs_in,
input [COLOR_DEPTH-1:0] r_in,
input [COLOR_DEPTH-1:0] g_in,
input [COLOR_DEPTH-1:0] b_in,
// output interface
output reg hs_out,
output reg vs_out,
output reg [5:0] r_out,
output reg [5:0] g_out,
output reg [5:0] b_out
);
parameter HCNT_WIDTH = 9;
parameter COLOR_DEPTH = 6;
// pixel clock divider
reg [1:0] i_div;
reg ce_x1, ce_x2;
always @(posedge clk_sys) begin
reg last_hs_in;
last_hs_in <= hs_in;
if(last_hs_in & !hs_in) begin
i_div <= 2'b00;
end else begin
i_div <= i_div + 2'd1;
end
end
always @(*) begin
if (!ce_divider) begin
ce_x1 = (i_div == 2'b01);
ce_x2 = i_div[0];
end else begin
ce_x1 = i_div[0];
ce_x2 = 1'b1;
end
end
assign pixel_ena = bypass ? ce_x1 : ce_x2;
// --------------------- create output signals -----------------
// latch everything once more to make it glitch free and apply scanline effect
reg scanline;
reg [5:0] r;
reg [5:0] g;
reg [5:0] b;
always @(*) begin
if (COLOR_DEPTH == 6) begin
b = sd_out[5:0];
g = sd_out[11:6];
r = sd_out[17:12];
end else if (COLOR_DEPTH == 2) begin
b = {3{sd_out[1:0]}};
g = {3{sd_out[3:2]}};
r = {3{sd_out[5:4]}};
end else if (COLOR_DEPTH == 1) begin
b = {6{sd_out[0]}};
g = {6{sd_out[1]}};
r = {6{sd_out[2]}};
end else begin
b = { sd_out[COLOR_DEPTH-1:0], sd_out[COLOR_DEPTH-1 -:(6-COLOR_DEPTH)] };
g = { sd_out[COLOR_DEPTH*2-1:COLOR_DEPTH], sd_out[COLOR_DEPTH*2-1 -:(6-COLOR_DEPTH)] };
r = { sd_out[COLOR_DEPTH*3-1:COLOR_DEPTH*2], sd_out[COLOR_DEPTH*3-1 -:(6-COLOR_DEPTH)] };
end
end
always @(posedge clk_sys) begin
if(bypass) begin
r_out <= r;
g_out <= g;
b_out <= b;
hs_out <= hs_sd;
vs_out <= vs_sd;
end else if(ce_x2) begin
hs_out <= hs_sd;
vs_out <= vs_sd;
// reset scanlines at every new screen
if(vs_out != vs_in) scanline <= 0;
// toggle scanlines at begin of every hsync
if(hs_out && !hs_sd) scanline <= !scanline;
// if no scanlines or not a scanline
if(!scanline || !scanlines) begin
r_out <= r;
g_out <= g;
b_out <= b;
end else begin
case(scanlines)
1: begin // reduce 25% = 1/2 + 1/4
r_out <= {1'b0, r[5:1]} + {2'b00, r[5:2] };
g_out <= {1'b0, g[5:1]} + {2'b00, g[5:2] };
b_out <= {1'b0, b[5:1]} + {2'b00, b[5:2] };
end
2: begin // reduce 50% = 1/2
r_out <= {1'b0, r[5:1]};
g_out <= {1'b0, g[5:1]};
b_out <= {1'b0, b[5:1]};
end
3: begin // reduce 75% = 1/4
r_out <= {2'b00, r[5:2]};
g_out <= {2'b00, g[5:2]};
b_out <= {2'b00, b[5:2]};
end
endcase
end
end
end
// scan doubler output register
wire [COLOR_DEPTH*3-1:0] sd_out = bypass ? sd_bypass_out : sd_buffer_out;
// ==================================================================
// ======================== the line buffers ========================
// ==================================================================
// 2 lines of 2**HCNT_WIDTH pixels 3*COLOR_DEPTH bit RGB
(* ramstyle = "no_rw_check" *) reg [COLOR_DEPTH*3-1:0] sd_buffer[2*2**HCNT_WIDTH];
// use alternating sd_buffers when storing/reading data
reg line_toggle;
// total hsync time (in 16MHz cycles), hs_total reaches 1024
reg [HCNT_WIDTH-1:0] hs_max;
reg [HCNT_WIDTH-1:0] hs_rise;
reg [HCNT_WIDTH-1:0] hcnt;
always @(posedge clk_sys) begin
reg hsD, vsD;
if(ce_x1) begin
hsD <= hs_in;
// falling edge of hsync indicates start of line
if(hsD && !hs_in) begin
hs_max <= hcnt;
hcnt <= 0;
end else begin
hcnt <= hcnt + 1'd1;
end
// save position of rising edge
if(!hsD && hs_in) hs_rise <= hcnt;
vsD <= vs_in;
if(vsD != vs_in) line_toggle <= 0;
// begin of incoming hsync
if(hsD && !hs_in) line_toggle <= !line_toggle;
sd_buffer[{line_toggle, hcnt}] <= {r_in, g_in, b_in};
end
end
// ==================================================================
// ==================== output timing generation ====================
// ==================================================================
reg [COLOR_DEPTH*3-1:0] sd_buffer_out, sd_bypass_out;
reg [HCNT_WIDTH-1:0] sd_hcnt;
reg hs_sd, vs_sd;
// timing generation runs 32 MHz (twice the input signal analysis speed)
always @(posedge clk_sys) begin
reg hsD;
if(ce_x2) begin
hsD <= hs_in;
// output counter synchronous to input and at twice the rate
sd_hcnt <= sd_hcnt + 1'd1;
if(hsD && !hs_in) sd_hcnt <= hs_max;
if(sd_hcnt == hs_max) sd_hcnt <= 0;
// replicate horizontal sync at twice the speed
if(sd_hcnt == hs_max) hs_sd <= 0;
if(sd_hcnt == hs_rise) hs_sd <= 1;
// read data from line sd_buffer
sd_buffer_out <= sd_buffer[{~line_toggle, sd_hcnt}];
vs_sd <= vs_in;
end
if(bypass) begin
sd_bypass_out <= {r_in, g_in, b_in};
hs_sd <= hs_in;
vs_sd <= vs_in;
end
end
endmodule

619
rtl/mist-modules/sd_card.v Normal file
View File

@ -0,0 +1,619 @@
//
// sd_card.v
//
// This file implelents a sd card for the MIST board since on the board
// the SD card is connected to the ARM IO controller and the FPGA has no
// direct connection to the SD card. This file provides a SD card like
// interface to the IO controller easing porting of cores that expect
// a direct interface to the SD card.
//
// Copyright (c) 2014 Till Harbaum <till@harbaum.org>
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the Lesser 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 <http://www.gnu.org/licenses/>.
//
// http://elm-chan.org/docs/mmc/mmc_e.html
module sd_card (
input clk_sys,
// link to user_io for io controller
output reg[31:0] sd_lba,
output reg sd_rd,
output reg sd_wr,
input sd_ack,
input sd_ack_conf,
output sd_conf,
output sd_sdhc,
input img_mounted,
input [63:0] img_size,
output reg sd_busy = 0,
// data coming in from io controller
input [7:0] sd_buff_dout,
input sd_buff_wr,
// data going out to io controller
output [7:0] sd_buff_din,
input [8:0] sd_buff_addr,
// configuration input
// in case of a VHD file, this will determine the SD Card type returned to the SPI master
// in case of a pass-through, the firmware will display a warning if SDHC is not allowed,
// but the card inserted is SDHC
input allow_sdhc,
input sd_cs,
input sd_sck,
input sd_sdi,
output reg sd_sdo
);
wire [31:0] OCR = { 1'b1, sdhc, 6'h0, 9'h1f, 15'h0 }; // bit31 = finished powerup
// bit30 = 1 -> high capaciry card (sdhc)
// 15-23 supported voltage range
wire [7:0] READ_DATA_TOKEN = 8'hfe;
// number of bytes to wait after a command before sending the reply
localparam NCR=4;
localparam RD_STATE_IDLE = 3'd0;
localparam RD_STATE_WAIT_BUSY = 3'd1;
localparam RD_STATE_WAIT_IO = 3'd2;
localparam RD_STATE_SEND_TOKEN = 3'd3;
localparam RD_STATE_SEND_DATA = 3'd4;
localparam RD_STATE_DELAY = 3'd5;
reg [2:0] read_state = RD_STATE_IDLE;
localparam WR_STATE_IDLE = 3'd0;
localparam WR_STATE_EXP_DTOKEN = 3'd1;
localparam WR_STATE_RECV_DATA = 3'd2;
localparam WR_STATE_RECV_CRC0 = 3'd3;
localparam WR_STATE_RECV_CRC1 = 3'd4;
localparam WR_STATE_SEND_DRESP = 3'd5;
localparam WR_STATE_WRITE = 3'd6;
localparam WR_STATE_BUSY = 3'd7;
reg [2:0] write_state = WR_STATE_IDLE;
reg card_is_reset = 1'b0; // flag that card has received a reset command
reg [6:0] sbuf;
reg cmd55;
reg terminate_cmd = 1'b0;
reg [7:0] cmd = 8'h00;
reg [2:0] bit_cnt = 3'd0; // counts bits 0-7 0-7 ...
reg [3:0] byte_cnt= 4'd15; // counts bytes
reg [4:0] delay_cnt;
reg wr_first;
reg [39:0] args;
reg [7:0] reply;
reg [7:0] reply0, reply1, reply2, reply3;
reg [3:0] reply_len;
// ------------------------- SECTOR BUFFER -----------------------
// the buffer itself. Can hold two sectors
reg [9:0] buffer_ptr;
wire [7:0] buffer_dout;
reg [7:0] buffer_din;
reg buffer_write_strobe;
reg sd_buff_sel;
sd_card_dpram #(8, 10) buffer_dpram
(
.clock_a (clk_sys),
.address_a ({sd_buff_sel, sd_buff_addr}),
.data_a (sd_buff_dout),
.wren_a (sd_buff_wr & sd_ack & read_state != RD_STATE_IDLE),
.q_a (sd_buff_din),
.clock_b (clk_sys),
.address_b (buffer_ptr),
.data_b (buffer_din),
.wren_b (buffer_write_strobe),
.q_b (buffer_dout)
);
wire [7:0] WRITE_DATA_RESPONSE = 8'h05;
// ------------------------- CSD/CID BUFFER ----------------------
reg [7:0] conf;
assign sd_conf = sd_configuring;
reg sd_configuring = 1;
reg [4:0] conf_buff_ptr;
reg [7:0] conf_byte_orig;
reg[255:0] csdcid;
reg vhd = 0;
reg [40:0] vhd_size;
// conf[0]==1 -> io controller is using an sdhc card
wire sd_has_sdhc = conf[0];
assign sd_sdhc = (allow_sdhc & sd_has_sdhc) | vhd; // report to user_io
wire sdhc = allow_sdhc & (sd_has_sdhc | vhd); // used internally
always @(posedge clk_sys) begin
reg old_mounted;
if (sd_buff_wr & sd_ack_conf) begin
if (sd_buff_addr == 32) begin
conf <= sd_buff_dout;
sd_configuring <= 0;
end
else csdcid[(31-sd_buff_addr) << 3 +:8] <= sd_buff_dout;
end
conf_byte_orig <= csdcid[(31-conf_buff_ptr) << 3 +:8];
old_mounted <= img_mounted;
if (~old_mounted & img_mounted) begin
vhd <= |img_size;
vhd_size <= img_size[40:0];
end
end
// CSD V1.0 no. of blocks = c_size ** (c_size_mult + 2)
wire [127:0] csd_sd = {
8'h00, // CSD_STRUCTURE + reserved
8'h2d, // TAAC
8'd0, // NSAC
8'h32, // TRAN_SPEED
12'h5b5, // CCC
4'h9, // READ_BL_LEN
1'b1, 1'b0, 1'b0, 1'b0, // READ_BL_PARTIAL, WRITE_BLK_MISALIGN, READ_BLK_MISALIGN, DSR_IMP
2'd0, vhd_size[29:18], // reserved + C_SIZE
3'b111, // VDD_R_CURR_MIN
3'b110, // VDD_R_CURR_MAX
3'b111, // VDD_W_CURR_MIN
3'b110, // VDD_W_CURR_MAX
3'd7, // C_SIZE_MULT
1'b1, // ERASE_BLK_EN
7'd127, // SECTOR_SIZE
7'd0, // WP_GRP_SIZE
1'b0, // WP_GRP_ENABLE,
2'b00, // reserved,
3'd5, // R2W_FACTOR,
4'h9, // WRITE_BL_LEN,
1'b0, // WRITE_BL_PARTIAL,
5'd0, // reserved,
8'd0,
7'h67, // CRC (wrong, but usually not checked)
1'b1 };
// CSD V2.0 size = (c_size + 1) * 512K
wire [127:0] csd_sdhc = {
8'h40, // CSD_STRUCTURE + reserved
8'h0e, // TAAC
8'd0, // NSAC
8'h32, // TRAN_SPEED
12'h5b5, // CCC
4'h9, // READ_BL_LEN
1'b0, 1'b0, 1'b0, 1'b0, // READ_BL_PARTIAL, WRITE_BLK_MISALIGN, READ_BLK_MISALIGN, DSR_IMP
6'd0, // reserved
vhd_size[40:19] - 1'd1, // C_SIZE
1'b0, // reserved
1'b1, // ERASE_BLK_EN
7'd127, // SECTOR_SIZE
7'd0, // WP_GRP_SIZE
1'b0, // WP_GRP_ENABLE,
2'b00, // reserved,
3'd2, // R2W_FACTOR,
4'h9, // WRITE_BL_LEN,
1'b0, // WRITE_BL_PARTIAL,
5'd0, // reserved,
8'd0,
7'h78, // CRC (wrong, but usually not checked)
1'b1 };
wire [7:0] conf_byte = (!conf_buff_ptr[4] | !vhd) ? conf_byte_orig : // CID or CSD if not VHD
sdhc ? csd_sdhc[(15-conf_buff_ptr[3:0]) << 3 +:8] :
csd_sd[(15-conf_buff_ptr[3:0]) << 3 +:8];
always@(posedge clk_sys) begin
reg old_sd_sck;
reg [5:0] ack;
ack <= {ack[4:0], sd_ack};
if(ack[5:4] == 'b01) { sd_rd, sd_wr } <= 2'b00;
if(ack[5:4] == 'b10) sd_busy <= 0;
buffer_write_strobe <= 0;
if (buffer_write_strobe) buffer_ptr <= buffer_ptr + 1'd1;
old_sd_sck <= sd_sck;
// advance transmitter state machine on falling sck edge, so data is valid on the
// rising edge
// ----------------- spi transmitter --------------------
if(sd_cs == 0 && old_sd_sck && ~sd_sck) begin
sd_sdo <= 1'b1; // default: send 1's (busy/wait)
if(byte_cnt == 5+NCR) begin
sd_sdo <= reply[~bit_cnt];
if(bit_cnt == 7) begin
// these three commands all have a reply_len of 0 and will thus
// not send more than a single reply byte
// CMD9: SEND_CSD
// CMD10: SEND_CID
if((cmd == 8'h49)||(cmd == 8'h4a))
read_state <= RD_STATE_SEND_TOKEN; // jump directly to data transmission
// CMD17: READ_SINGLE_BLOCK
// CMD18: READ_MULTIPLE_BLOCK
if((cmd == 8'h51 || cmd == 8'h52) && !terminate_cmd)
read_state <= RD_STATE_WAIT_BUSY; // start waiting for data from io controller
end
end
else if((reply_len > 0) && (byte_cnt == 5+NCR+1))
sd_sdo <= reply0[~bit_cnt];
else if((reply_len > 1) && (byte_cnt == 5+NCR+2))
sd_sdo <= reply1[~bit_cnt];
else if((reply_len > 2) && (byte_cnt == 5+NCR+3))
sd_sdo <= reply2[~bit_cnt];
else if((reply_len > 3) && (byte_cnt == 5+NCR+4))
sd_sdo <= reply3[~bit_cnt];
// ---------- read state machine processing -------------
case(read_state)
RD_STATE_IDLE: ;
// don't do anything
// wait until the IO controller is free and issue a read
RD_STATE_WAIT_BUSY:
if (~sd_busy) begin
sd_buff_sel <= 0;
sd_lba <= sdhc?args[39:8]:{9'd0, args[39:17]};
sd_rd <= 1; // trigger request to io controller
sd_busy <= 1;
read_state <= RD_STATE_WAIT_IO;
end
// waiting for io controller to return data
RD_STATE_WAIT_IO: begin
buffer_ptr <= 0;
if(~sd_busy) begin
if (terminate_cmd) begin
cmd <= 0;
read_state <= RD_STATE_IDLE;
end else if (bit_cnt == 7)
read_state <= RD_STATE_SEND_TOKEN;
end
end
// send data token
RD_STATE_SEND_TOKEN: begin
if(~sd_busy) begin
sd_sdo <= READ_DATA_TOKEN[~bit_cnt];
if(bit_cnt == 7) begin
read_state <= RD_STATE_SEND_DATA; // next: send data
conf_buff_ptr <= (cmd == 8'h4a) ? 5'h0 : 5'h10;
end
end
end
// send data
RD_STATE_SEND_DATA: begin
if(cmd == 8'h51 || (cmd == 8'h52 && !terminate_cmd)) // CMD17: READ_SINGLE_BLOCK, CMD18: READ_MULTIPLE_BLOCK
sd_sdo <= buffer_dout[~bit_cnt];
else if(cmd == 8'h49 || cmd == 8'h4a) // CMD9: SEND_CSD, CMD10: SEND CID
sd_sdo <= conf_byte[~bit_cnt];
if(bit_cnt == 7) begin
// sent 512 sector data bytes?
if((cmd == 8'h51) && &buffer_ptr[8:0])
read_state <= RD_STATE_IDLE; // next: send crc. It's ignored so return to idle state
if (cmd == 8'h52) begin
if (terminate_cmd) begin
read_state <= RD_STATE_IDLE;
cmd <= 0;
end else if (buffer_ptr[8:0] == 10) begin
// prefetch the next sector into the other buffer
sd_lba <= sd_lba + 1'd1;
sd_rd <= 1;
sd_busy <= 1;
sd_buff_sel <= !sd_buff_sel;
end else if (&buffer_ptr[8:0]) begin
delay_cnt <= 20;
read_state <= RD_STATE_DELAY;
end
end
// sent 16 cid/csd data bytes?
if(((cmd == 8'h49)||(cmd == 8'h4a)) && conf_buff_ptr[3:0] == 4'h0f) // && (buffer_rptr == 16))
read_state <= RD_STATE_IDLE; // return to idle state
buffer_ptr <= buffer_ptr + 1'd1;
conf_buff_ptr<= conf_buff_ptr+ 1'd1;
end
end
RD_STATE_DELAY:
if(bit_cnt == 7) begin
if (delay_cnt == 0) begin
read_state <= RD_STATE_SEND_TOKEN;
end else begin
delay_cnt <= delay_cnt - 1'd1;
end
end
endcase
// ------------------ write support ----------------------
// send write data response
if(write_state == WR_STATE_SEND_DRESP)
sd_sdo <= WRITE_DATA_RESPONSE[~bit_cnt];
// busy after write until the io controller sends ack
if(write_state == WR_STATE_WRITE || write_state == WR_STATE_BUSY)
sd_sdo <= 1'b0;
end
// spi receiver
// cs is active low
if(sd_cs == 1) begin
bit_cnt <= 3'd0;
terminate_cmd <= 0;
cmd <= 0;
read_state <= RD_STATE_IDLE;
reply_len <= 0;
end else if (~old_sd_sck & sd_sck) begin
bit_cnt <= bit_cnt + 3'd1;
// assemble byte
if(bit_cnt != 7)
sbuf[6:0] <= { sbuf[5:0], sd_sdi };
else begin
// finished reading one byte
// byte counter runs against 15 byte boundary
if(byte_cnt != 15)
byte_cnt <= byte_cnt + 4'd1;
// byte_cnt > 6 -> complete command received
// first byte of valid command is 01xxxxxx
// don't accept new commands (except STOP TRANSMISSION) once a write or read command has been accepted
if((byte_cnt > (reply_len == 0 ? 5 : (5+NCR+reply_len))) &&
(write_state == WR_STATE_IDLE) &&
(read_state == RD_STATE_IDLE || (read_state != RD_STATE_IDLE && { sbuf, sd_sdi} == 8'h4c)) &&
sbuf[6:5] == 2'b01)
begin
byte_cnt <= 4'd0;
terminate_cmd <= 0;
if ({ sbuf, sd_sdi } == 8'h4c) begin
terminate_cmd <= 1;
end else
cmd <= { sbuf, sd_sdi};
end
// parse additional command bytes
if(byte_cnt == 0) args[39:32] <= { sbuf, sd_sdi};
if(byte_cnt == 1) args[31:24] <= { sbuf, sd_sdi};
if(byte_cnt == 2) args[23:16] <= { sbuf, sd_sdi};
if(byte_cnt == 3) args[15:8] <= { sbuf, sd_sdi};
if(byte_cnt == 4) args[7:0] <= { sbuf, sd_sdi};
// last byte received, evaluate
if(byte_cnt == 5) begin
// default:
reply <= 8'h04; // illegal command
reply_len <= 4'd0; // no extra reply bytes
cmd55 <= 0;
// CMD0: GO_IDLE_STATE
if(cmd == 8'h40) begin
card_is_reset <= 1'b1;
reply <= 8'h01; // ok, busy
end
// every other command is only accepted after a reset
else if(card_is_reset) begin
// CMD12: STOP_TRANSMISSION
if (terminate_cmd)
reply <= 8'h00; // ok
else case(cmd)
// CMD1: SEND_OP_COND
8'h41: reply <= 8'h00; // ok, not busy
// CMD8: SEND_IF_COND (V2 only)
8'h48: begin
reply <= 8'h01; // ok, busy
reply0 <= 8'h00;
reply1 <= 8'h00;
reply2 <= { 4'b0, args[19:16] };
reply3 <= args[15:8];
reply_len <= 4'd4;
end
// CMD9: SEND_CSD
8'h49: reply <= 8'h00; // ok
// CMD10: SEND_CID
8'h4a: reply <= 8'h00; // ok
// CMD13: SEND_STATUS
8'h4d: begin
reply <= 8'h00; // ok
reply0 <=8'h00;
reply_len <= 4'd1;
end
// CMD16: SET_BLOCKLEN
8'h50:
// we only support a block size of 512
if(args[39:8] == 32'd512)
reply <= 8'h00; // ok
else
reply <= 8'h40; // parmeter error
// CMD17: READ_SINGLE_BLOCK
8'h51: reply <= 8'h00; // ok
// CMD18: READ_MULTIPLE_BLOCK
8'h52: reply <= 8'h00; // ok
// CMD24: WRITE_BLOCK
// CMD25: WRITE_MULTIPLE_BLOCKS
8'h58, 8'h59: begin
reply <= 8'h00; // ok
buffer_ptr <= 0;
wr_first <= 1;
write_state <= WR_STATE_EXP_DTOKEN; // expect data token
end
// ACMD41: APP_SEND_OP_COND
8'h69: if(cmd55) begin
reply <= 8'h00; // ok, not busy
end
// CMD55: APP_COND
8'h77: begin
reply <= 8'h01; // ok, busy
cmd55 <= 1;
end
// CMD58: READ_OCR
8'h7a: begin
reply <= 8'h00; // ok
reply0 <= OCR[31:24]; // bit 30 = 1 -> high capacity card
reply1 <= OCR[23:16];
reply2 <= OCR[15:8];
reply3 <= OCR[7:0];
reply_len <= 4'd4;
end
endcase
end
end
end
end
// ---------- handle write -----------
case(write_state)
// don't do anything in idle state
WR_STATE_IDLE: ;
// waiting for data token
WR_STATE_EXP_DTOKEN:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) begin
if({ sbuf, sd_sdi} == 8'hfd && cmd == 8'h59)
// stop multiple write (and wait until the last write finishes)
write_state <= WR_STATE_BUSY;
else
if({ sbuf, sd_sdi} == ((cmd == 8'h59) ? 8'hfc : 8'hfe))
write_state <= WR_STATE_RECV_DATA;
end
// transfer 512 bytes
WR_STATE_RECV_DATA:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) begin
// push one byte into local buffer
buffer_write_strobe <= 1'b1;
buffer_din <= { sbuf, sd_sdi };
// all bytes written?
if(&buffer_ptr[8:0])
write_state <= WR_STATE_RECV_CRC0;
end
// transfer 1st crc byte
WR_STATE_RECV_CRC0:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) write_state <= WR_STATE_RECV_CRC1;
// transfer 2nd crc byte
WR_STATE_RECV_CRC1:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) write_state <= WR_STATE_SEND_DRESP;
// send data response
WR_STATE_SEND_DRESP:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) write_state <= WR_STATE_WRITE;
WR_STATE_WRITE:
if (~sd_busy) begin
if (wr_first) begin
sd_buff_sel <= 0;
sd_lba <= sdhc?args[39:8]:{9'd0, args[39:17]};
wr_first <= 0;
end else begin
sd_buff_sel <= !sd_buff_sel;
sd_lba <= sd_lba + 1'd1;
end
sd_wr <= 1; // trigger write request to io controller
sd_busy <= 1;
if (sd_cs || cmd == 8'h58)
write_state <= WR_STATE_BUSY;
else
write_state <= WR_STATE_EXP_DTOKEN; // multi-sector writes
end
WR_STATE_BUSY:
if (~sd_busy) write_state <= WR_STATE_IDLE;
default: ;
endcase
end
endmodule
module sd_card_dpram #(parameter DATAWIDTH=8, ADDRWIDTH=9)
(
input clock_a,
input [ADDRWIDTH-1:0] address_a,
input [DATAWIDTH-1:0] data_a,
input wren_a,
output reg [DATAWIDTH-1:0] q_a,
input clock_b,
input [ADDRWIDTH-1:0] address_b,
input [DATAWIDTH-1:0] data_b,
input wren_b,
output reg [DATAWIDTH-1:0] q_b
);
reg [DATAWIDTH-1:0] ram[0:(1<<ADDRWIDTH)-1];
always @(posedge clock_a) begin
q_a <= ram[address_a];
if(wren_a) begin
q_a <= data_a;
ram[address_a] <= data_a;
end
end
always @(posedge clock_b) begin
q_b <= ram[address_b];
if(wren_b) begin
q_b <= data_b;
ram[address_b] <= data_b;
end
end
endmodule

748
rtl/mist-modules/user_io.v Normal file
View File

@ -0,0 +1,748 @@
//
// user_io.v
//
// user_io for the MiST board
// http://code.google.com/p/mist-board/
//
// Copyright (c) 2014 Till Harbaum <till@harbaum.org>
//
// 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 <http://www.gnu.org/licenses/>.
//
// parameter STRLEN and the actual length of conf_str have to match
module user_io (
input [(8*STRLEN)-1:0] conf_str,
output [9:0] conf_addr, // RAM address for config string, if STRLEN=0
input [7:0] conf_chr,
input clk_sys, // clock for system-related messages (kbd, joy, etc...)
input clk_sd, // clock for SD-card related messages
input SPI_CLK,
input SPI_SS_IO,
output reg SPI_MISO,
input SPI_MOSI,
output reg [31:0] joystick_0,
output reg [31:0] joystick_1,
output reg [31:0] joystick_2,
output reg [31:0] joystick_3,
output reg [31:0] joystick_4,
output reg [31:0] joystick_analog_0,
output reg [31:0] joystick_analog_1,
output [1:0] buttons,
output [1:0] switches,
output scandoubler_disable,
output ypbpr,
output no_csync,
output reg [63:0] status,
output reg [6:0] core_mod, // core variant, sent before the config string is requested
// RTC data from IO controller
// sec, min, hour, date, month, year, day (BCD)
output reg [63:0] rtc,
// connection to sd card emulation
input [31:0] sd_lba,
input [SD_IMAGES-1:0] sd_rd,
input [SD_IMAGES-1:0] sd_wr,
output reg sd_ack,
output reg sd_ack_conf,
input sd_conf,
input sd_sdhc,
output reg [7:0] sd_dout, // valid on rising edge of sd_dout_strobe
output reg sd_dout_strobe,
input [7:0] sd_din,
output reg sd_din_strobe,
output reg [8:0] sd_buff_addr,
output reg [SD_IMAGES-1:0] img_mounted, // rising edge if a new image is mounted
output reg [63:0] img_size, // size of image in bytes
// ps2 keyboard/mouse emulation
output ps2_kbd_clk,
output reg ps2_kbd_data,
input ps2_kbd_clk_i,
input ps2_kbd_data_i,
output ps2_mouse_clk,
output reg ps2_mouse_data,
input ps2_mouse_clk_i,
input ps2_mouse_data_i,
// keyboard data
output reg key_pressed, // 1-make (pressed), 0-break (released)
output reg key_extended, // extended code
output reg [7:0] key_code, // key scan code
output reg key_strobe, // key data valid
// mouse data
output reg [8:0] mouse_x,
output reg [8:0] mouse_y,
output reg [3:0] mouse_z,
output reg [7:0] mouse_flags, // YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn
output reg mouse_strobe, // mouse data is valid on mouse_strobe
output reg mouse_idx, // which mouse?
// serial com port
input [7:0] serial_data,
input serial_strobe
);
parameter STRLEN=0; // config string length
parameter PS2DIV=100; // master clock divider for psk2_kbd/mouse clk
parameter ROM_DIRECT_UPLOAD=0; // direct upload used for file uploads from the ARM
parameter SD_IMAGES=2; // number of block-access images (max. 4 supported in current firmware)
parameter PS2BIDIR=0; // bi-directional PS2 interface
parameter FEATURES=0; // requested features from the firmware
localparam W = $clog2(SD_IMAGES);
reg [6:0] sbuf;
reg [7:0] cmd;
reg [2:0] bit_cnt; // counts bits 0-7 0-7 ...
reg [9:0] byte_cnt; // counts bytes
reg [7:0] but_sw;
reg [2:0] stick_idx;
assign buttons = but_sw[1:0];
assign switches = but_sw[3:2];
assign scandoubler_disable = but_sw[4];
assign ypbpr = but_sw[5];
assign no_csync = but_sw[6];
assign conf_addr = byte_cnt;
// this variant of user_io is for 8 bit cores (type == a4) only
// bit 4 indicates ROM direct upload capability
wire [7:0] core_type = ROM_DIRECT_UPLOAD ? 8'hb4 : 8'ha4;
reg [W:0] drive_sel;
always begin
integer i;
drive_sel = 0;
for(i = 0; i < SD_IMAGES; i = i + 1) if(sd_rd[i] | sd_wr[i]) drive_sel = i[W:0];
end
// command byte read by the io controller
wire [7:0] sd_cmd = { 4'h6, sd_conf, sd_sdhc, sd_wr[drive_sel], sd_rd[drive_sel] };
wire spi_sck = SPI_CLK;
// ---------------- PS2 ---------------------
// 16 byte fifos to store ps2 bytes
localparam PS2_FIFO_BITS = 4;
reg ps2_clk;
always @(posedge clk_sys) begin
integer cnt;
cnt <= cnt + 1'd1;
if(cnt == PS2DIV) begin
ps2_clk <= ~ps2_clk;
cnt <= 0;
end
end
// keyboard
reg [7:0] ps2_kbd_fifo [(2**PS2_FIFO_BITS)-1:0];
reg [PS2_FIFO_BITS-1:0] ps2_kbd_wptr;
reg [PS2_FIFO_BITS-1:0] ps2_kbd_rptr;
// ps2 transmitter state machine
reg [3:0] ps2_kbd_tx_state;
reg [7:0] ps2_kbd_tx_byte;
reg ps2_kbd_parity;
// ps2 receiver state machine
reg [3:0] ps2_kbd_rx_state = 0;
reg [1:0] ps2_kbd_rx_start = 0;
reg [7:0] ps2_kbd_rx_byte = 0;
reg ps2_kbd_rx_strobe = 0;
assign ps2_kbd_clk = ps2_clk || (ps2_kbd_tx_state == 0 && ps2_kbd_rx_state == 0);
// ps2 transmitter/receiver
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
// Sends a command to the IO controller if bidirectional mode is enabled.
always@(posedge clk_sys) begin : ps2_kbd
reg ps2_clkD;
reg ps2_clk_iD, ps2_dat_iD;
reg ps2_kbd_r_inc;
// send data
ps2_clkD <= ps2_clk;
if (~ps2_clkD & ps2_clk) begin
ps2_kbd_r_inc <= 1'b0;
if(ps2_kbd_r_inc)
ps2_kbd_rptr <= ps2_kbd_rptr + 1'd1;
// transmitter is idle?
if(ps2_kbd_tx_state == 0) begin
ps2_kbd_data <= 1;
// data in fifo present?
if(ps2_kbd_wptr != ps2_kbd_rptr && (ps2_kbd_clk_i | !PS2BIDIR)) begin
// load tx register from fifo
ps2_kbd_tx_byte <= ps2_kbd_fifo[ps2_kbd_rptr];
ps2_kbd_r_inc <= 1'b1;
// reset parity
ps2_kbd_parity <= 1'b1;
// start transmitter
ps2_kbd_tx_state <= 4'd1;
// put start bit on data line
ps2_kbd_data <= 1'b0; // start bit is 0
end
end else begin
// transmission of 8 data bits
if((ps2_kbd_tx_state >= 1)&&(ps2_kbd_tx_state < 9)) begin
ps2_kbd_data <= ps2_kbd_tx_byte[0]; // data bits
ps2_kbd_tx_byte[6:0] <= ps2_kbd_tx_byte[7:1]; // shift down
if(ps2_kbd_tx_byte[0])
ps2_kbd_parity <= !ps2_kbd_parity;
end
// transmission of parity
if(ps2_kbd_tx_state == 9)
ps2_kbd_data <= ps2_kbd_parity;
// transmission of stop bit
if(ps2_kbd_tx_state == 10)
ps2_kbd_data <= 1'b1; // stop bit is 1
// advance state machine
if(ps2_kbd_tx_state < 11)
ps2_kbd_tx_state <= ps2_kbd_tx_state + 4'd1;
else
ps2_kbd_tx_state <= 4'd0;
end
end
if (PS2BIDIR) begin
ps2_clk_iD <= ps2_kbd_clk_i;
ps2_dat_iD <= ps2_kbd_data_i;
// receive command
case (ps2_kbd_rx_start)
2'd0:
// first: host pulls down the clock line
if (ps2_clk_iD & ~ps2_kbd_clk_i) ps2_kbd_rx_start <= 1;
2'd1:
// second: host pulls down the data line, while releasing the clock
if (ps2_dat_iD && !ps2_kbd_data_i) ps2_kbd_rx_start <= 2'd2;
// if it releases the clock without pulling down the data line: goto 0
else if (ps2_kbd_clk_i) ps2_kbd_rx_start <= 0;
2'd2:
if (ps2_clkD && ~ps2_clk) begin
ps2_kbd_rx_state <= 4'd1;
ps2_kbd_rx_start <= 0;
end
default: ;
endcase
// host data is valid after the rising edge of the clock
if(ps2_kbd_rx_state != 0 && ~ps2_clkD && ps2_clk) begin
ps2_kbd_rx_state <= ps2_kbd_rx_state + 1'd1;
if (ps2_kbd_rx_state == 9) ;// parity
else if (ps2_kbd_rx_state == 10) begin
ps2_kbd_data <= 0; // ack the received byte
end else if (ps2_kbd_rx_state == 11) begin
ps2_kbd_rx_state <= 0;
ps2_kbd_rx_strobe <= ~ps2_kbd_rx_strobe;
end else begin
ps2_kbd_rx_byte <= {ps2_kbd_data_i, ps2_kbd_rx_byte[7:1]};
end
end
end
end
// mouse
reg [7:0] ps2_mouse_fifo [(2**PS2_FIFO_BITS)-1:0];
reg [PS2_FIFO_BITS-1:0] ps2_mouse_wptr;
reg [PS2_FIFO_BITS-1:0] ps2_mouse_rptr;
// ps2 transmitter state machine
reg [3:0] ps2_mouse_tx_state;
reg [7:0] ps2_mouse_tx_byte;
reg ps2_mouse_parity;
// ps2 receiver state machine
reg [3:0] ps2_mouse_rx_state = 0;
reg [1:0] ps2_mouse_rx_start = 0;
reg [7:0] ps2_mouse_rx_byte = 0;
reg ps2_mouse_rx_strobe = 0;
assign ps2_mouse_clk = ps2_clk || (ps2_mouse_tx_state == 0 && ps2_mouse_rx_state == 0);
// ps2 transmitter/receiver
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
// Sends a command to the IO controller if bidirectional mode is enabled.
always@(posedge clk_sys) begin : ps2_mouse
reg ps2_clkD;
reg ps2_clk_iD, ps2_dat_iD;
reg ps2_mouse_r_inc;
ps2_clkD <= ps2_clk;
if (~ps2_clkD & ps2_clk) begin
ps2_mouse_r_inc <= 1'b0;
if(ps2_mouse_r_inc)
ps2_mouse_rptr <= ps2_mouse_rptr + 1'd1;
// transmitter is idle?
if(ps2_mouse_tx_state == 0) begin
ps2_mouse_data <= 1;
// data in fifo present?
if(ps2_mouse_wptr != ps2_mouse_rptr && (ps2_mouse_clk_i | !PS2BIDIR)) begin
// load tx register from fifo
ps2_mouse_tx_byte <= ps2_mouse_fifo[ps2_mouse_rptr];
ps2_mouse_r_inc <= 1'b1;
// reset parity
ps2_mouse_parity <= 1'b1;
// start transmitter
ps2_mouse_tx_state <= 4'd1;
// put start bit on data line
ps2_mouse_data <= 1'b0; // start bit is 0
end
end else begin
// transmission of 8 data bits
if((ps2_mouse_tx_state >= 1)&&(ps2_mouse_tx_state < 9)) begin
ps2_mouse_data <= ps2_mouse_tx_byte[0]; // data bits
ps2_mouse_tx_byte[6:0] <= ps2_mouse_tx_byte[7:1]; // shift down
if(ps2_mouse_tx_byte[0])
ps2_mouse_parity <= !ps2_mouse_parity;
end
// transmission of parity
if(ps2_mouse_tx_state == 9)
ps2_mouse_data <= ps2_mouse_parity;
// transmission of stop bit
if(ps2_mouse_tx_state == 10)
ps2_mouse_data <= 1'b1; // stop bit is 1
// advance state machine
if(ps2_mouse_tx_state < 11)
ps2_mouse_tx_state <= ps2_mouse_tx_state + 4'd1;
else
ps2_mouse_tx_state <= 4'd0;
end
end
if (PS2BIDIR) begin
ps2_clk_iD <= ps2_mouse_clk_i;
ps2_dat_iD <= ps2_mouse_data_i;
// receive command
case (ps2_mouse_rx_start)
2'd0:
// first: host pulls down the clock line
if (ps2_clk_iD & ~ps2_mouse_clk_i) ps2_mouse_rx_start <= 1;
2'd1:
// second: host pulls down the data line, while releasing the clock
if (ps2_dat_iD && !ps2_mouse_data_i) ps2_mouse_rx_start <= 2'd2;
// if it releases the clock without pulling down the data line: goto 0
else if (ps2_mouse_clk_i) ps2_mouse_rx_start <= 0;
2'd2:
if (ps2_clkD && ~ps2_clk) begin
ps2_mouse_rx_state <= 4'd1;
ps2_mouse_rx_start <= 0;
end
default: ;
endcase
// host data is valid after the rising edge of the clock
if(ps2_mouse_rx_state != 0 && ~ps2_clkD && ps2_clk) begin
ps2_mouse_rx_state <= ps2_mouse_rx_state + 1'd1;
if (ps2_mouse_rx_state == 9) ;// parity
else if (ps2_mouse_rx_state == 10) begin
ps2_mouse_data <= 0; // ack the received byte
end else if (ps2_mouse_rx_state == 11) begin
ps2_mouse_rx_state <= 0;
ps2_mouse_rx_strobe <= ~ps2_mouse_rx_strobe;
end else begin
ps2_mouse_rx_byte <= {ps2_mouse_data_i, ps2_mouse_rx_byte[7:1]};
end
end
end
end
// fifo to receive serial data from core to be forwarded to io controller
// 16 byte fifo to store serial bytes
localparam SERIAL_OUT_FIFO_BITS = 6;
reg [7:0] serial_out_fifo [(2**SERIAL_OUT_FIFO_BITS)-1:0];
reg [SERIAL_OUT_FIFO_BITS-1:0] serial_out_wptr;
reg [SERIAL_OUT_FIFO_BITS-1:0] serial_out_rptr;
wire serial_out_data_available = serial_out_wptr != serial_out_rptr;
wire [7:0] serial_out_byte = serial_out_fifo[serial_out_rptr] /* synthesis keep */;
wire [7:0] serial_out_status = { 7'b1000000, serial_out_data_available};
// status[0] is reset signal from io controller and is thus used to flush
// the fifo
always @(posedge serial_strobe or posedge status[0]) begin : serial_out
if(status[0] == 1) begin
serial_out_wptr <= 0;
end else begin
serial_out_fifo[serial_out_wptr] <= serial_data;
serial_out_wptr <= serial_out_wptr + 1'd1;
end
end
always@(negedge spi_sck or posedge status[0]) begin : serial_in
if(status[0] == 1) begin
serial_out_rptr <= 0;
end else begin
if((byte_cnt != 0) && (cmd == 8'h1b)) begin
// read last bit -> advance read pointer
if((bit_cnt == 7) && !byte_cnt[0] && serial_out_data_available)
serial_out_rptr <= serial_out_rptr + 1'd1;
end
end
end
// SPI bit and byte counters
always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_counter
if(SPI_SS_IO == 1) begin
bit_cnt <= 0;
byte_cnt <= 0;
end else begin
if((bit_cnt == 7)&&(~&byte_cnt))
byte_cnt <= byte_cnt + 8'd1;
bit_cnt <= bit_cnt + 1'd1;
end
end
// SPI transmitter FPGA -> IO
reg [7:0] spi_byte_out;
always@(negedge spi_sck or posedge SPI_SS_IO) begin : spi_byteout
if(SPI_SS_IO == 1) begin
SPI_MISO <= 1'bZ;
end else begin
SPI_MISO <= spi_byte_out[~bit_cnt];
end
end
always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_transmitter
reg [31:0] sd_lba_r;
reg [W:0] drive_sel_r;
reg ps2_kbd_rx_strobeD;
reg ps2_mouse_rx_strobeD;
if(SPI_SS_IO == 1) begin
spi_byte_out <= core_type;
end else begin
// read the command byte to choose the response
if(bit_cnt == 7) begin
if(!byte_cnt) cmd <= {sbuf, SPI_MOSI};
spi_byte_out <= 0;
case({(!byte_cnt) ? {sbuf, SPI_MOSI} : cmd})
// PS2 keyboard command
8'h0e: if (byte_cnt == 0) begin
ps2_kbd_rx_strobeD <= ps2_kbd_rx_strobe;
//echo the command code if there's a byte to send, indicating the core supports the command
spi_byte_out <= (ps2_kbd_rx_strobe ^ ps2_kbd_rx_strobeD) ? 8'h0e : 8'h00;
end else spi_byte_out <= ps2_kbd_rx_byte;
// PS2 mouse command
8'h0f: if (byte_cnt == 0) begin
ps2_mouse_rx_strobeD <= ps2_mouse_rx_strobe;
//echo the command code if there's a byte to send, indicating the core supports the command
spi_byte_out <= (ps2_mouse_rx_strobe ^ ps2_mouse_rx_strobeD) ? 8'h0f : 8'h00;
end else spi_byte_out <= ps2_mouse_rx_byte;
// reading config string
8'h14: if (STRLEN == 0) spi_byte_out <= conf_chr; else
if(byte_cnt < STRLEN) spi_byte_out <= conf_str[(STRLEN - byte_cnt - 1)<<3 +:8];
// reading sd card status
8'h16: if(byte_cnt == 0) begin
spi_byte_out <= sd_cmd;
sd_lba_r <= sd_lba;
drive_sel_r <= drive_sel;
end
else if(byte_cnt == 1) spi_byte_out <= drive_sel_r;
else if(byte_cnt < 6) spi_byte_out <= sd_lba_r[(5-byte_cnt)<<3 +:8];
// reading sd card write data
8'h18: spi_byte_out <= sd_din;
8'h1b:
// send alternating flag byte and data
if(byte_cnt[0]) spi_byte_out <= serial_out_status;
else spi_byte_out <= serial_out_byte;
// core features
8'h80:
if (byte_cnt == 0) spi_byte_out <= 8'h80;
else spi_byte_out <= FEATURES[(4-byte_cnt)<<3 +:8];
endcase
end
end
end
// SPI receiver IO -> FPGA
reg spi_receiver_strobe_r = 0;
reg spi_transfer_end_r = 1;
reg [7:0] spi_byte_in;
// Read at spi_sck clock domain, assemble bytes for transferring to clk_sys
always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_receiver
if(SPI_SS_IO == 1) begin
spi_transfer_end_r <= 1;
end else begin
spi_transfer_end_r <= 0;
if(bit_cnt != 7)
sbuf[6:0] <= { sbuf[5:0], SPI_MOSI };
// finished reading a byte, prepare to transfer to clk_sys
if(bit_cnt == 7) begin
spi_byte_in <= { sbuf, SPI_MOSI};
spi_receiver_strobe_r <= ~spi_receiver_strobe_r;
end
end
end
// Process bytes from SPI at the clk_sys domain
always @(posedge clk_sys) begin : cmd_block
reg spi_receiver_strobe;
reg spi_transfer_end;
reg spi_receiver_strobeD;
reg spi_transfer_endD;
reg [7:0] acmd;
reg [7:0] abyte_cnt; // counts bytes
reg [7:0] mouse_flags_r;
reg [7:0] mouse_x_r;
reg [7:0] mouse_y_r;
reg key_pressed_r;
reg key_extended_r;
//synchronize between SPI and sys clock domains
spi_receiver_strobeD <= spi_receiver_strobe_r;
spi_receiver_strobe <= spi_receiver_strobeD;
spi_transfer_endD <= spi_transfer_end_r;
spi_transfer_end <= spi_transfer_endD;
key_strobe <= 0;
mouse_strobe <= 0;
if (spi_transfer_end) begin
abyte_cnt <= 8'd0;
end else if (spi_receiver_strobeD ^ spi_receiver_strobe) begin
if(~&abyte_cnt)
abyte_cnt <= abyte_cnt + 8'd1;
if(abyte_cnt == 0) begin
acmd <= spi_byte_in;
end else begin
case(acmd)
// buttons and switches
8'h01: but_sw <= spi_byte_in;
8'h60: if (abyte_cnt < 5) joystick_0[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
8'h61: if (abyte_cnt < 5) joystick_1[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
8'h62: if (abyte_cnt < 5) joystick_2[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
8'h63: if (abyte_cnt < 5) joystick_3[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
8'h64: if (abyte_cnt < 5) joystick_4[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
8'h70,8'h71: begin
// store incoming ps2 mouse bytes
if (abyte_cnt < 4) begin
ps2_mouse_fifo[ps2_mouse_wptr] <= spi_byte_in;
ps2_mouse_wptr <= ps2_mouse_wptr + 1'd1;
end
if (abyte_cnt == 1) mouse_flags_r <= spi_byte_in;
else if (abyte_cnt == 2) mouse_x_r <= spi_byte_in;
else if (abyte_cnt == 3) mouse_y_r <= spi_byte_in;
else if (abyte_cnt == 4) begin
// flags: YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn
mouse_flags <= mouse_flags_r;
mouse_x <= { mouse_flags_r[4], mouse_x_r };
mouse_y <= { mouse_flags_r[5], mouse_y_r };
mouse_z <= spi_byte_in[3:0];
mouse_idx <= acmd[0];
mouse_strobe <= 1;
end
end
8'h05: begin
// store incoming ps2 keyboard bytes
ps2_kbd_fifo[ps2_kbd_wptr] <= spi_byte_in;
ps2_kbd_wptr <= ps2_kbd_wptr + 1'd1;
if (abyte_cnt == 1) begin
key_extended_r <= 0;
key_pressed_r <= 1;
end
if (spi_byte_in == 8'he0) key_extended_r <= 1'b1;
else if (spi_byte_in == 8'hf0) key_pressed_r <= 1'b0;
else begin
key_extended <= key_extended_r && abyte_cnt != 1;
key_pressed <= key_pressed_r || abyte_cnt == 1;
key_code <= spi_byte_in;
key_strobe <= 1'b1;
end
end
// joystick analog
8'h1a: begin
// first byte is joystick index
if(abyte_cnt == 1)
stick_idx <= spi_byte_in[2:0];
else if(abyte_cnt == 2) begin
// second byte is x axis
if(stick_idx == 0)
joystick_analog_0[15:8] <= spi_byte_in;
else if(stick_idx == 1)
joystick_analog_1[15:8] <= spi_byte_in;
end else if(abyte_cnt == 3) begin
// third byte is y axis
if(stick_idx == 0)
joystick_analog_0[7:0] <= spi_byte_in;
else if(stick_idx == 1)
joystick_analog_1[7:0] <= spi_byte_in;
end else if(abyte_cnt == 4) begin
// fourth byte is 2nd x axis
if(stick_idx == 0)
joystick_analog_0[31:24] <= spi_byte_in;
else if(stick_idx == 1)
joystick_analog_1[31:24] <= spi_byte_in;
end else if(abyte_cnt == 5) begin
// fifth byte is 2nd y axis
if(stick_idx == 0)
joystick_analog_0[23:16] <= spi_byte_in;
else if(stick_idx == 1)
joystick_analog_1[23:16] <= spi_byte_in;
end
end
8'h15: status <= spi_byte_in;
// status, 64bit version
8'h1e: if(abyte_cnt<9) status[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
// core variant
8'h21: core_mod <= spi_byte_in[6:0];
// RTC
8'h22: if(abyte_cnt<9) rtc[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
endcase
end
end
end
// Process SD-card related bytes from SPI at the clk_sd domain
always @(posedge clk_sd) begin : sd_block
reg spi_receiver_strobe;
reg spi_transfer_end;
reg spi_receiver_strobeD;
reg spi_transfer_endD;
reg [SD_IMAGES-1:0] sd_wrD;
reg [7:0] acmd;
reg [7:0] abyte_cnt; // counts bytes
//synchronize between SPI and sd clock domains
spi_receiver_strobeD <= spi_receiver_strobe_r;
spi_receiver_strobe <= spi_receiver_strobeD;
spi_transfer_endD <= spi_transfer_end_r;
spi_transfer_end <= spi_transfer_endD;
if(sd_dout_strobe) begin
sd_dout_strobe<= 0;
if(~&sd_buff_addr) sd_buff_addr <= sd_buff_addr + 1'b1;
end
sd_din_strobe<= 0;
sd_wrD <= sd_wr;
// fetch the first byte immediately after the write command seen
if (|(~sd_wrD & sd_wr)) begin
sd_buff_addr <= 0;
sd_din_strobe <= 1;
end
img_mounted <= 0;
if (spi_transfer_end) begin
abyte_cnt <= 8'd0;
sd_ack <= 1'b0;
sd_ack_conf <= 1'b0;
sd_buff_addr <= 0;
end else if (spi_receiver_strobeD ^ spi_receiver_strobe) begin
if(~&abyte_cnt)
abyte_cnt <= abyte_cnt + 8'd1;
if(abyte_cnt == 0) begin
acmd <= spi_byte_in;
if(spi_byte_in == 8'h18) begin
sd_din_strobe <= 1'b1;
if(~&sd_buff_addr) sd_buff_addr <= sd_buff_addr + 1'b1;
end
if(spi_byte_in == 8'h19)
sd_ack_conf <= 1'b1;
if((spi_byte_in == 8'h17) || (spi_byte_in == 8'h18))
sd_ack <= 1'b1;
end else begin
case(acmd)
// send sector IO -> FPGA
8'h17,
// send SD config IO -> FPGA
8'h19: begin
// flag that download begins
sd_dout_strobe <= 1'b1;
sd_dout <= spi_byte_in;
end
// send sector FPGA -> IO
8'h18: begin
if(~&sd_buff_addr) begin
sd_din_strobe <= 1'b1;
sd_buff_addr <= sd_buff_addr + 1'b1;
end
end
8'h1c: img_mounted[spi_byte_in[W:0]] <= 1;
// send image info
8'h1d: if(abyte_cnt<9) img_size[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
endcase
end
end
end
endmodule

View File

@ -0,0 +1,454 @@
//
//
// Copyright (c) 2012-2013 Ludvig Strigeus
// Copyright (c) 2017 Sorgelig
//
// This program is GPL Licensed. See COPYING for the full license.
//
//
////////////////////////////////////////////////////////////////////////////////////////////////////////
// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
`define BITS_TO_FIT(N) ( \
N <= 2 ? 0 : \
N <= 4 ? 1 : \
N <= 8 ? 2 : \
N <= 16 ? 3 : \
N <= 32 ? 4 : \
N <= 64 ? 5 : \
N <= 128 ? 6 : \
N <= 256 ? 7 : \
N <= 512 ? 8 : \
N <=1024 ? 9 : 10 )
module hq2x_in #(parameter LENGTH, parameter DWIDTH)
(
input clk,
input [AWIDTH:0] rdaddr,
input rdbuf,
output[DWIDTH:0] q,
input [AWIDTH:0] wraddr,
input wrbuf,
input [DWIDTH:0] data,
input wren
);
localparam AWIDTH = `BITS_TO_FIT(LENGTH);
wire [DWIDTH:0] out[2];
assign q = out[rdbuf];
hq2x_buf #(.NUMWORDS(LENGTH), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf0(clk,data,rdaddr,wraddr,wren && (wrbuf == 0),out[0]);
hq2x_buf #(.NUMWORDS(LENGTH), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf1(clk,data,rdaddr,wraddr,wren && (wrbuf == 1),out[1]);
endmodule
module hq2x_out #(parameter LENGTH, parameter DWIDTH)
(
input clk,
input [AWIDTH:0] rdaddr,
input [1:0] rdbuf,
output[DWIDTH:0] q,
input [AWIDTH:0] wraddr,
input [1:0] wrbuf,
input [DWIDTH:0] data,
input wren
);
localparam AWIDTH = `BITS_TO_FIT(LENGTH*2);
wire [DWIDTH:0] out[4];
assign q = out[rdbuf];
hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf0(clk,data,rdaddr,wraddr,wren && (wrbuf == 0),out[0]);
hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf1(clk,data,rdaddr,wraddr,wren && (wrbuf == 1),out[1]);
hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf2(clk,data,rdaddr,wraddr,wren && (wrbuf == 2),out[2]);
hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf3(clk,data,rdaddr,wraddr,wren && (wrbuf == 3),out[3]);
endmodule
module hq2x_buf #(parameter NUMWORDS, parameter AWIDTH, parameter DWIDTH)
(
input clock,
input [DWIDTH:0] data,
input [AWIDTH:0] rdaddress,
input [AWIDTH:0] wraddress,
input wren,
output [DWIDTH:0] q
);
altsyncram altsyncram_component (
.address_a (wraddress),
.clock0 (clock),
.data_a (data),
.wren_a (wren),
.address_b (rdaddress),
.q_b(q),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clock1 (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.data_b ({(DWIDTH+1){1'b1}}),
.eccstatus (),
.q_a (),
.rden_a (1'b1),
.rden_b (1'b1),
.wren_b (1'b0));
defparam
altsyncram_component.address_aclr_b = "NONE",
altsyncram_component.address_reg_b = "CLOCK0",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.intended_device_family = "Cyclone III",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = NUMWORDS,
altsyncram_component.numwords_b = NUMWORDS,
altsyncram_component.operation_mode = "DUAL_PORT",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_b = "UNREGISTERED",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE",
altsyncram_component.widthad_a = AWIDTH+1,
altsyncram_component.widthad_b = AWIDTH+1,
altsyncram_component.width_a = DWIDTH+1,
altsyncram_component.width_b = DWIDTH+1,
altsyncram_component.width_byteena_a = 1;
endmodule
////////////////////////////////////////////////////////////////////////////////////////////////////////
module DiffCheck
(
input [17:0] rgb1,
input [17:0] rgb2,
output result
);
wire [5:0] r = rgb1[5:1] - rgb2[5:1];
wire [5:0] g = rgb1[11:7] - rgb2[11:7];
wire [5:0] b = rgb1[17:13] - rgb2[17:13];
wire [6:0] t = $signed(r) + $signed(b);
wire [6:0] gx = {g[5], g};
wire [7:0] y = $signed(t) + $signed(gx);
wire [6:0] u = $signed(r) - $signed(b);
wire [7:0] v = $signed({g, 1'b0}) - $signed(t);
// if y is inside (-24..24)
wire y_inside = (y < 8'h18 || y >= 8'he8);
// if u is inside (-4, 4)
wire u_inside = (u < 7'h4 || u >= 7'h7c);
// if v is inside (-6, 6)
wire v_inside = (v < 8'h6 || v >= 8'hfA);
assign result = !(y_inside && u_inside && v_inside);
endmodule
module InnerBlend
(
input [8:0] Op,
input [5:0] A,
input [5:0] B,
input [5:0] C,
output [5:0] O
);
function [8:0] mul6x3;
input [5:0] op1;
input [2:0] op2;
begin
mul6x3 = 9'd0;
if(op2[0]) mul6x3 = mul6x3 + op1;
if(op2[1]) mul6x3 = mul6x3 + {op1, 1'b0};
if(op2[2]) mul6x3 = mul6x3 + {op1, 2'b00};
end
endfunction
wire OpOnes = Op[4];
wire [8:0] Amul = mul6x3(A, Op[7:5]);
wire [8:0] Bmul = mul6x3(B, {Op[3:2], 1'b0});
wire [8:0] Cmul = mul6x3(C, {Op[1:0], 1'b0});
wire [8:0] At = Amul;
wire [8:0] Bt = (OpOnes == 0) ? Bmul : {3'b0, B};
wire [8:0] Ct = (OpOnes == 0) ? Cmul : {3'b0, C};
wire [9:0] Res = {At, 1'b0} + Bt + Ct;
assign O = Op[8] ? A : Res[9:4];
endmodule
module Blend
(
input [5:0] rule,
input disable_hq2x,
input [17:0] E,
input [17:0] A,
input [17:0] B,
input [17:0] D,
input [17:0] F,
input [17:0] H,
output [17:0] Result
);
reg [1:0] input_ctrl;
reg [8:0] op;
localparam BLEND0 = 9'b1_xxx_x_xx_xx; // 0: A
localparam BLEND1 = 9'b0_110_0_10_00; // 1: (A * 12 + B * 4) >> 4
localparam BLEND2 = 9'b0_100_0_10_10; // 2: (A * 8 + B * 4 + C * 4) >> 4
localparam BLEND3 = 9'b0_101_0_10_01; // 3: (A * 10 + B * 4 + C * 2) >> 4
localparam BLEND4 = 9'b0_110_0_01_01; // 4: (A * 12 + B * 2 + C * 2) >> 4
localparam BLEND5 = 9'b0_010_0_11_11; // 5: (A * 4 + (B + C) * 6) >> 4
localparam BLEND6 = 9'b0_111_1_xx_xx; // 6: (A * 14 + B + C) >> 4
localparam AB = 2'b00;
localparam AD = 2'b01;
localparam DB = 2'b10;
localparam BD = 2'b11;
wire is_diff;
DiffCheck diff_checker(rule[1] ? B : H, rule[0] ? D : F, is_diff);
always @* begin
case({!is_diff, rule[5:2]})
1,17: {op, input_ctrl} = {BLEND1, AB};
2,18: {op, input_ctrl} = {BLEND1, DB};
3,19: {op, input_ctrl} = {BLEND1, BD};
4,20: {op, input_ctrl} = {BLEND2, DB};
5,21: {op, input_ctrl} = {BLEND2, AB};
6,22: {op, input_ctrl} = {BLEND2, AD};
8: {op, input_ctrl} = {BLEND0, 2'bxx};
9: {op, input_ctrl} = {BLEND0, 2'bxx};
10: {op, input_ctrl} = {BLEND0, 2'bxx};
11: {op, input_ctrl} = {BLEND1, AB};
12: {op, input_ctrl} = {BLEND1, AB};
13: {op, input_ctrl} = {BLEND1, AB};
14: {op, input_ctrl} = {BLEND1, DB};
15: {op, input_ctrl} = {BLEND1, BD};
24: {op, input_ctrl} = {BLEND2, DB};
25: {op, input_ctrl} = {BLEND5, DB};
26: {op, input_ctrl} = {BLEND6, DB};
27: {op, input_ctrl} = {BLEND2, DB};
28: {op, input_ctrl} = {BLEND4, DB};
29: {op, input_ctrl} = {BLEND5, DB};
30: {op, input_ctrl} = {BLEND3, BD};
31: {op, input_ctrl} = {BLEND3, DB};
default: {op, input_ctrl} = 11'bx;
endcase
// Setting op[8] effectively disables HQ2X because blend will always return E.
if (disable_hq2x) op[8] = 1;
end
// Generate inputs to the inner blender. Valid combinations.
// 00: E A B
// 01: E A D
// 10: E D B
// 11: E B D
wire [17:0] Input1 = E;
wire [17:0] Input2 = !input_ctrl[1] ? A :
!input_ctrl[0] ? D : B;
wire [17:0] Input3 = !input_ctrl[0] ? B : D;
InnerBlend inner_blend1(op, Input1[5:0], Input2[5:0], Input3[5:0], Result[5:0]);
InnerBlend inner_blend2(op, Input1[11:6], Input2[11:6], Input3[11:6], Result[11:6]);
InnerBlend inner_blend3(op, Input1[17:12], Input2[17:12], Input3[17:12], Result[17:12]);
endmodule
////////////////////////////////////////////////////////////////////////////////////////////////////
module Hq2x #(parameter LENGTH, parameter HALF_DEPTH)
(
input clk,
input ce_x4,
input [DWIDTH:0] inputpixel,
input mono,
input disable_hq2x,
input reset_frame,
input reset_line,
input [1:0] read_y,
input [AWIDTH+1:0] read_x,
output [DWIDTH:0] outpixel
);
localparam AWIDTH = `BITS_TO_FIT(LENGTH);
localparam DWIDTH = HALF_DEPTH ? 8 : 17;
wire [5:0] hqTable[256] = '{
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 47, 35, 23, 15, 55, 39,
19, 19, 26, 58, 19, 19, 26, 58, 23, 15, 35, 35, 23, 15, 7, 35,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 55, 39, 23, 15, 51, 43,
19, 19, 26, 58, 19, 19, 26, 58, 23, 15, 51, 35, 23, 15, 7, 43,
19, 19, 26, 11, 19, 19, 26, 11, 23, 61, 35, 35, 23, 61, 51, 35,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 51, 35,
19, 19, 26, 11, 19, 19, 26, 11, 23, 61, 7, 35, 23, 61, 7, 43,
19, 19, 26, 11, 19, 19, 26, 58, 23, 15, 51, 35, 23, 61, 7, 43,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 47, 35, 23, 15, 55, 39,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 51, 35,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 55, 39, 23, 15, 51, 43,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 39, 23, 15, 7, 43,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 51, 39,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 7, 35,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 7, 43,
19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 7, 35, 23, 15, 7, 43
};
reg [17:0] Prev0, Prev1, Prev2, Curr0, Curr1, Next0, Next1, Next2;
reg [17:0] A, B, D, F, G, H;
reg [7:0] pattern, nextpatt;
reg [1:0] i;
reg [7:0] y;
wire curbuf = y[0];
reg prevbuf = 0;
wire iobuf = !curbuf;
wire diff0, diff1;
DiffCheck diffcheck0(Curr1, (i == 0) ? Prev0 : (i == 1) ? Curr0 : (i == 2) ? Prev2 : Next1, diff0);
DiffCheck diffcheck1(Curr1, (i == 0) ? Prev1 : (i == 1) ? Next0 : (i == 2) ? Curr2 : Next2, diff1);
wire [7:0] new_pattern = {diff1, diff0, pattern[7:2]};
wire [17:0] X = (i == 0) ? A : (i == 1) ? Prev1 : (i == 2) ? Next1 : G;
wire [17:0] blend_result;
Blend blender(hqTable[nextpatt], disable_hq2x, Curr0, X, B, D, F, H, blend_result);
reg Curr2_addr1;
reg [AWIDTH:0] Curr2_addr2;
wire [17:0] Curr2 = HALF_DEPTH ? h2rgb(Curr2tmp) : Curr2tmp;
wire [DWIDTH:0] Curr2tmp;
reg [AWIDTH:0] wrin_addr2;
reg [DWIDTH:0] wrpix;
reg wrin_en;
function [17:0] h2rgb;
input [8:0] v;
begin
h2rgb = mono ? {v[5:3],v[2:0], v[5:3],v[2:0], v[5:3],v[2:0]} : {v[8:6],v[8:6],v[5:3],v[5:3],v[2:0],v[2:0]};
end
endfunction
function [8:0] rgb2h;
input [17:0] v;
begin
rgb2h = mono ? {3'b000, v[17:15], v[14:12]} : {v[17:15], v[11:9], v[5:3]};
end
endfunction
hq2x_in #(.LENGTH(LENGTH), .DWIDTH(DWIDTH)) hq2x_in
(
.clk(clk),
.rdaddr(Curr2_addr2),
.rdbuf(Curr2_addr1),
.q(Curr2tmp),
.wraddr(wrin_addr2),
.wrbuf(iobuf),
.data(wrpix),
.wren(wrin_en)
);
reg [1:0] wrout_addr1;
reg [AWIDTH+1:0] wrout_addr2;
reg wrout_en;
reg [DWIDTH:0] wrdata;
hq2x_out #(.LENGTH(LENGTH), .DWIDTH(DWIDTH)) hq2x_out
(
.clk(clk),
.rdaddr(read_x),
.rdbuf(read_y),
.q(outpixel),
.wraddr(wrout_addr2),
.wrbuf(wrout_addr1),
.data(wrdata),
.wren(wrout_en)
);
always @(posedge clk) begin
reg [AWIDTH:0] offs;
reg old_reset_line;
reg old_reset_frame;
wrout_en <= 0;
wrin_en <= 0;
if(ce_x4) begin
pattern <= new_pattern;
if(~&offs) begin
if (i == 0) begin
Curr2_addr1 <= prevbuf;
Curr2_addr2 <= offs;
end
if (i == 1) begin
Prev2 <= Curr2;
Curr2_addr1 <= curbuf;
Curr2_addr2 <= offs;
end
if (i == 2) begin
Next2 <= HALF_DEPTH ? h2rgb(inputpixel) : inputpixel;
wrpix <= inputpixel;
wrin_addr2 <= offs;
wrin_en <= 1;
end
if (i == 3) begin
offs <= offs + 1'd1;
end
if(HALF_DEPTH) wrdata <= rgb2h(blend_result);
else wrdata <= blend_result;
wrout_addr1 <= {curbuf, i[1]};
wrout_addr2 <= {offs, i[1]^i[0]};
wrout_en <= 1;
end
if(i==3) begin
nextpatt <= {new_pattern[7:6], new_pattern[3], new_pattern[5], new_pattern[2], new_pattern[4], new_pattern[1:0]};
{A, G} <= {Prev0, Next0};
{B, F, H, D} <= {Prev1, Curr2, Next1, Curr0};
{Prev0, Prev1} <= {Prev1, Prev2};
{Curr0, Curr1} <= {Curr1, Curr2};
{Next0, Next1} <= {Next1, Next2};
end else begin
nextpatt <= {nextpatt[5], nextpatt[3], nextpatt[0], nextpatt[6], nextpatt[1], nextpatt[7], nextpatt[4], nextpatt[2]};
{B, F, H, D} <= {F, H, D, B};
end
i <= i + 1'b1;
if(old_reset_line && ~reset_line) begin
old_reset_frame <= reset_frame;
offs <= 0;
i <= 0;
y <= y + 1'd1;
prevbuf <= curbuf;
if(old_reset_frame & ~reset_frame) begin
y <= 0;
prevbuf <= 0;
end
end
old_reset_line <= reset_line;
end
end
endmodule // Hq2x

View File

@ -0,0 +1,491 @@
//
// mist_io.v
//
// mist_io for the MiST board
// http://code.google.com/p/mist-board/
//
// Copyright (c) 2014 Till Harbaum <till@harbaum.org>
//
// 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 <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////
//
// Use buffer to access SD card. It's time-critical part.
// Made module synchroneous with 2 clock domains: clk_sys and SPI_SCK
// (Sorgelig)
//
// for synchronous projects default value for PS2DIV is fine for any frequency of system clock.
// clk_ps2 = clk_sys/(PS2DIV*2)
//
module mist_io #(parameter STRLEN=0, parameter PS2DIV=100)
(
// parameter STRLEN and the actual length of conf_str have to match
input [(8*STRLEN)-1:0] conf_str,
// Global clock. It should be around 100MHz (higher is better).
input clk_sys,
// Global SPI clock from ARM. 24MHz
input SPI_SCK,
input CONF_DATA0,
input SPI_SS2,
output SPI_DO,
input SPI_DI,
output reg [7:0] joystick_0,
output reg [7:0] joystick_1,
output reg [15:0] joystick_analog_0,
output reg [15:0] joystick_analog_1,
output [1:0] buttons,
output [1:0] switches,
output scandoubler_disable,
output ypbpr,
output reg [31:0] status,
// SD config
input sd_conf,
input sd_sdhc,
output img_mounted, // signaling that new image has been mounted
output reg [31:0] img_size, // size of image in bytes
// SD block level access
input [31:0] sd_lba,
input sd_rd,
input sd_wr,
output reg sd_ack,
output reg sd_ack_conf,
// SD byte level access. Signals for 2-PORT altsyncram.
output reg [8:0] sd_buff_addr,
output reg [7:0] sd_buff_dout,
input [7:0] sd_buff_din,
output reg sd_buff_wr,
// ps2 keyboard emulation
output ps2_kbd_clk,
output reg ps2_kbd_data,
output ps2_mouse_clk,
output reg ps2_mouse_data,
input ps2_caps_led,
// ARM -> FPGA download
output reg ioctl_download = 0, // signal indicating an active download
output reg [7:0] ioctl_index, // menu index used to upload the file
output ioctl_wr,
output reg [24:0] ioctl_addr,
output reg [7:0] ioctl_dout
);
reg [7:0] b_data;
reg [6:0] sbuf;
reg [7:0] cmd;
reg [2:0] bit_cnt; // counts bits 0-7 0-7 ...
reg [9:0] byte_cnt; // counts bytes
reg [7:0] but_sw;
reg [2:0] stick_idx;
reg mount_strobe = 0;
assign img_mounted = mount_strobe;
assign buttons = but_sw[1:0];
assign switches = but_sw[3:2];
assign scandoubler_disable = but_sw[4];
assign ypbpr = but_sw[5];
wire [7:0] spi_dout = { sbuf, SPI_DI};
// this variant of user_io is for 8 bit cores (type == a4) only
wire [7:0] core_type = 8'ha4;
// command byte read by the io controller
wire [7:0] sd_cmd = { 4'h5, sd_conf, sd_sdhc, sd_wr, sd_rd };
reg spi_do;
assign SPI_DO = CONF_DATA0 ? 1'bZ : spi_do;
wire [7:0] kbd_led = { 2'b01, 4'b0000, ps2_caps_led, 1'b1};
// drive MISO only when transmitting core id
always@(negedge SPI_SCK) begin
if(!CONF_DATA0) begin
// first byte returned is always core type, further bytes are
// command dependent
if(byte_cnt == 0) begin
spi_do <= core_type[~bit_cnt];
end else begin
case(cmd)
// reading config string
8'h14: begin
// returning a byte from string
if(byte_cnt < STRLEN + 1) spi_do <= conf_str[{STRLEN - byte_cnt,~bit_cnt}];
else spi_do <= 0;
end
// reading sd card status
8'h16: begin
if(byte_cnt == 1) spi_do <= sd_cmd[~bit_cnt];
else if((byte_cnt >= 2) && (byte_cnt < 6)) spi_do <= sd_lba[{5-byte_cnt, ~bit_cnt}];
else spi_do <= 0;
end
// reading sd card write data
8'h18:
spi_do <= b_data[~bit_cnt];
// reading keyboard LED status
8'h1f:
spi_do <= kbd_led[~bit_cnt];
default:
spi_do <= 0;
endcase
end
end
end
reg b_wr2,b_wr3;
always @(negedge clk_sys) begin
b_wr3 <= b_wr2;
sd_buff_wr <= b_wr3;
end
// SPI receiver
always@(posedge SPI_SCK or posedge CONF_DATA0) begin
if(CONF_DATA0) begin
b_wr2 <= 0;
bit_cnt <= 0;
byte_cnt <= 0;
sd_ack <= 0;
sd_ack_conf <= 0;
end else begin
b_wr2 <= 0;
sbuf <= spi_dout[6:0];
bit_cnt <= bit_cnt + 1'd1;
if(bit_cnt == 5) begin
if (byte_cnt == 0) sd_buff_addr <= 0;
if((byte_cnt != 0) & (sd_buff_addr != 511)) sd_buff_addr <= sd_buff_addr + 1'b1;
if((byte_cnt == 1) & ((cmd == 8'h17) | (cmd == 8'h19))) sd_buff_addr <= 0;
end
// finished reading command byte
if(bit_cnt == 7) begin
if(~&byte_cnt) byte_cnt <= byte_cnt + 8'd1;
if(byte_cnt == 0) begin
cmd <= spi_dout;
if(spi_dout == 8'h19) begin
sd_ack_conf <= 1;
sd_buff_addr <= 0;
end
if((spi_dout == 8'h17) || (spi_dout == 8'h18)) begin
sd_ack <= 1;
sd_buff_addr <= 0;
end
if(spi_dout == 8'h18) b_data <= sd_buff_din;
mount_strobe <= 0;
end else begin
case(cmd)
// buttons and switches
8'h01: but_sw <= spi_dout;
8'h02: joystick_0 <= spi_dout;
8'h03: joystick_1 <= spi_dout;
// store incoming ps2 mouse bytes
8'h04: begin
ps2_mouse_fifo[ps2_mouse_wptr] <= spi_dout;
ps2_mouse_wptr <= ps2_mouse_wptr + 1'd1;
end
// store incoming ps2 keyboard bytes
8'h05: begin
ps2_kbd_fifo[ps2_kbd_wptr] <= spi_dout;
ps2_kbd_wptr <= ps2_kbd_wptr + 1'd1;
end
8'h15: status[7:0] <= spi_dout;
// send SD config IO -> FPGA
// flag that download begins
// sd card knows data is config if sd_dout_strobe is asserted
// with sd_ack still being inactive (low)
8'h19,
// send sector IO -> FPGA
// flag that download begins
8'h17: begin
sd_buff_dout <= spi_dout;
b_wr2 <= 1;
end
8'h18: b_data <= sd_buff_din;
// joystick analog
8'h1a: begin
// first byte is joystick index
if(byte_cnt == 1) stick_idx <= spi_dout[2:0];
else if(byte_cnt == 2) begin
// second byte is x axis
if(stick_idx == 0) joystick_analog_0[15:8] <= spi_dout;
else if(stick_idx == 1) joystick_analog_1[15:8] <= spi_dout;
end else if(byte_cnt == 3) begin
// third byte is y axis
if(stick_idx == 0) joystick_analog_0[7:0] <= spi_dout;
else if(stick_idx == 1) joystick_analog_1[7:0] <= spi_dout;
end
end
// notify image selection
8'h1c: mount_strobe <= 1;
// send image info
8'h1d: if(byte_cnt<5) img_size[(byte_cnt-1)<<3 +:8] <= spi_dout;
// status, 32bit version
8'h1e: if(byte_cnt<5) status[(byte_cnt-1)<<3 +:8] <= spi_dout;
default: ;
endcase
end
end
end
end
/////////////////////////////// PS2 ///////////////////////////////
// 8 byte fifos to store ps2 bytes
localparam PS2_FIFO_BITS = 3;
reg clk_ps2;
always @(negedge clk_sys) begin
integer cnt;
cnt <= cnt + 1'd1;
if(cnt == PS2DIV) begin
clk_ps2 <= ~clk_ps2;
cnt <= 0;
end
end
// keyboard
reg [7:0] ps2_kbd_fifo[1<<PS2_FIFO_BITS];
reg [PS2_FIFO_BITS-1:0] ps2_kbd_wptr;
reg [PS2_FIFO_BITS-1:0] ps2_kbd_rptr;
// ps2 transmitter state machine
reg [3:0] ps2_kbd_tx_state;
reg [7:0] ps2_kbd_tx_byte;
reg ps2_kbd_parity;
assign ps2_kbd_clk = clk_ps2 || (ps2_kbd_tx_state == 0);
// ps2 transmitter
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
reg ps2_kbd_r_inc;
always@(posedge clk_sys) begin
reg old_clk;
old_clk <= clk_ps2;
if(~old_clk & clk_ps2) begin
ps2_kbd_r_inc <= 0;
if(ps2_kbd_r_inc) ps2_kbd_rptr <= ps2_kbd_rptr + 1'd1;
// transmitter is idle?
if(ps2_kbd_tx_state == 0) begin
// data in fifo present?
if(ps2_kbd_wptr != ps2_kbd_rptr) begin
// load tx register from fifo
ps2_kbd_tx_byte <= ps2_kbd_fifo[ps2_kbd_rptr];
ps2_kbd_r_inc <= 1;
// reset parity
ps2_kbd_parity <= 1;
// start transmitter
ps2_kbd_tx_state <= 1;
// put start bit on data line
ps2_kbd_data <= 0; // start bit is 0
end
end else begin
// transmission of 8 data bits
if((ps2_kbd_tx_state >= 1)&&(ps2_kbd_tx_state < 9)) begin
ps2_kbd_data <= ps2_kbd_tx_byte[0]; // data bits
ps2_kbd_tx_byte[6:0] <= ps2_kbd_tx_byte[7:1]; // shift down
if(ps2_kbd_tx_byte[0])
ps2_kbd_parity <= !ps2_kbd_parity;
end
// transmission of parity
if(ps2_kbd_tx_state == 9) ps2_kbd_data <= ps2_kbd_parity;
// transmission of stop bit
if(ps2_kbd_tx_state == 10) ps2_kbd_data <= 1; // stop bit is 1
// advance state machine
if(ps2_kbd_tx_state < 11) ps2_kbd_tx_state <= ps2_kbd_tx_state + 1'd1;
else ps2_kbd_tx_state <= 0;
end
end
end
// mouse
reg [7:0] ps2_mouse_fifo[1<<PS2_FIFO_BITS];
reg [PS2_FIFO_BITS-1:0] ps2_mouse_wptr;
reg [PS2_FIFO_BITS-1:0] ps2_mouse_rptr;
// ps2 transmitter state machine
reg [3:0] ps2_mouse_tx_state;
reg [7:0] ps2_mouse_tx_byte;
reg ps2_mouse_parity;
assign ps2_mouse_clk = clk_ps2 || (ps2_mouse_tx_state == 0);
// ps2 transmitter
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
reg ps2_mouse_r_inc;
always@(posedge clk_sys) begin
reg old_clk;
old_clk <= clk_ps2;
if(~old_clk & clk_ps2) begin
ps2_mouse_r_inc <= 0;
if(ps2_mouse_r_inc) ps2_mouse_rptr <= ps2_mouse_rptr + 1'd1;
// transmitter is idle?
if(ps2_mouse_tx_state == 0) begin
// data in fifo present?
if(ps2_mouse_wptr != ps2_mouse_rptr) begin
// load tx register from fifo
ps2_mouse_tx_byte <= ps2_mouse_fifo[ps2_mouse_rptr];
ps2_mouse_r_inc <= 1;
// reset parity
ps2_mouse_parity <= 1;
// start transmitter
ps2_mouse_tx_state <= 1;
// put start bit on data line
ps2_mouse_data <= 0; // start bit is 0
end
end else begin
// transmission of 8 data bits
if((ps2_mouse_tx_state >= 1)&&(ps2_mouse_tx_state < 9)) begin
ps2_mouse_data <= ps2_mouse_tx_byte[0]; // data bits
ps2_mouse_tx_byte[6:0] <= ps2_mouse_tx_byte[7:1]; // shift down
if(ps2_mouse_tx_byte[0])
ps2_mouse_parity <= !ps2_mouse_parity;
end
// transmission of parity
if(ps2_mouse_tx_state == 9) ps2_mouse_data <= ps2_mouse_parity;
// transmission of stop bit
if(ps2_mouse_tx_state == 10) ps2_mouse_data <= 1; // stop bit is 1
// advance state machine
if(ps2_mouse_tx_state < 11) ps2_mouse_tx_state <= ps2_mouse_tx_state + 1'd1;
else ps2_mouse_tx_state <= 0;
end
end
end
/////////////////////////////// DOWNLOADING ///////////////////////////////
reg [7:0] data_w;
reg [24:0] addr_w;
reg rclk = 0;
localparam UIO_FILE_TX = 8'h53;
localparam UIO_FILE_TX_DAT = 8'h54;
localparam UIO_FILE_INDEX = 8'h55;
// data_io has its own SPI interface to the io controller
always@(posedge SPI_SCK, posedge SPI_SS2) begin
reg [6:0] sbuf;
reg [7:0] cmd;
reg [4:0] cnt;
reg [24:0] addr;
if(SPI_SS2) cnt <= 0;
else begin
rclk <= 0;
// don't shift in last bit. It is evaluated directly
// when writing to ram
if(cnt != 15) sbuf <= { sbuf[5:0], SPI_DI};
// increase target address after write
if(rclk) addr <= addr + 1'd1;
// 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};
// prepare/end transmission
if((cmd == UIO_FILE_TX) && (cnt == 15)) begin
// prepare
if(SPI_DI) begin
addr <= 0;
ioctl_download <= 1;
end else begin
addr_w <= addr;
ioctl_download <= 0;
end
end
// command 0x54: UIO_FILE_TX
if((cmd == UIO_FILE_TX_DAT) && (cnt == 15)) begin
addr_w <= addr;
data_w <= {sbuf, SPI_DI};
rclk <= 1;
end
// expose file (menu) index
if((cmd == UIO_FILE_INDEX) && (cnt == 15)) ioctl_index <= {sbuf, SPI_DI};
end
end
assign ioctl_wr = |ioctl_wrd;
reg [1:0] ioctl_wrd;
always@(negedge clk_sys) begin
reg rclkD, rclkD2;
rclkD <= rclk;
rclkD2 <= rclkD;
ioctl_wrd<= {ioctl_wrd[0],1'b0};
if(rclkD & ~rclkD2) begin
ioctl_dout <= data_w;
ioctl_addr <= addr_w;
ioctl_wrd <= 2'b11;
end
end
endmodule

179
rtl/mist-modules_old/osd.v Normal file
View File

@ -0,0 +1,179 @@
// A simple OSD implementation. Can be hooked up between a cores
// VGA output and the physical VGA pins
module osd (
// OSDs pixel clock, should be synchronous to cores pixel clock to
// avoid jitter.
input clk_sys,
// SPI interface
input SPI_SCK,
input SPI_SS3,
input SPI_DI,
// VGA signals coming from core
input [5:0] R_in,
input [5:0] G_in,
input [5:0] B_in,
input HSync,
input VSync,
// VGA signals going to video connector
output [5:0] R_out,
output [5:0] G_out,
output [5:0] B_out
);
parameter OSD_X_OFFSET = 10'd0;
parameter OSD_Y_OFFSET = 10'd0;
parameter OSD_COLOR = 3'd0;
localparam OSD_WIDTH = 10'd256;
localparam OSD_HEIGHT = 10'd128;
// *********************************************************************************
// spi client
// *********************************************************************************
// this core supports only the display related OSD commands
// of the minimig
reg osd_enable;
(* ramstyle = "no_rw_check" *) reg [7:0] osd_buffer[2047:0]; // the OSD buffer itself
// the OSD has its own SPI interface to the io controller
always@(posedge SPI_SCK, posedge SPI_SS3) begin
reg [4:0] cnt;
reg [10:0] bcnt;
reg [7:0] sbuf;
reg [7:0] cmd;
if(SPI_SS3) begin
cnt <= 0;
bcnt <= 0;
end else begin
sbuf <= {sbuf[6:0], SPI_DI};
// 0:7 is command, rest payload
if(cnt < 15) cnt <= cnt + 1'd1;
else cnt <= 8;
if(cnt == 7) begin
cmd <= {sbuf[6:0], SPI_DI};
// lower three command bits are line address
bcnt <= {sbuf[1:0], SPI_DI, 8'h00};
// command 0x40: OSDCMDENABLE, OSDCMDDISABLE
if(sbuf[6:3] == 4'b0100) osd_enable <= SPI_DI;
end
// command 0x20: OSDCMDWRITE
if((cmd[7:3] == 5'b00100) && (cnt == 15)) begin
osd_buffer[bcnt] <= {sbuf[6:0], SPI_DI};
bcnt <= bcnt + 1'd1;
end
end
end
// *********************************************************************************
// video timing and sync polarity anaylsis
// *********************************************************************************
// horizontal counter
reg [9:0] h_cnt;
reg [9:0] hs_low, hs_high;
wire hs_pol = hs_high < hs_low;
wire [9:0] dsp_width = hs_pol ? hs_low : hs_high;
// vertical counter
reg [9:0] v_cnt;
reg [9:0] vs_low, vs_high;
wire vs_pol = vs_high < vs_low;
wire [9:0] dsp_height = vs_pol ? vs_low : vs_high;
wire doublescan = (dsp_height>350);
reg ce_pix;
always @(negedge clk_sys) begin
integer cnt = 0;
integer pixsz, pixcnt;
reg hs;
cnt <= cnt + 1;
hs <= HSync;
pixcnt <= pixcnt + 1;
if(pixcnt == pixsz) pixcnt <= 0;
ce_pix <= !pixcnt;
if(hs && ~HSync) begin
cnt <= 0;
pixsz <= (cnt >> 9) - 1;
pixcnt <= 0;
ce_pix <= 1;
end
end
always @(posedge clk_sys) begin
reg hsD, hsD2;
reg vsD, vsD2;
if(ce_pix) begin
// bring hsync into local clock domain
hsD <= HSync;
hsD2 <= hsD;
// falling edge of HSync
if(!hsD && hsD2) begin
h_cnt <= 0;
hs_high <= h_cnt;
end
// rising edge of HSync
else if(hsD && !hsD2) begin
h_cnt <= 0;
hs_low <= h_cnt;
v_cnt <= v_cnt + 1'd1;
end else begin
h_cnt <= h_cnt + 1'd1;
end
vsD <= VSync;
vsD2 <= vsD;
// falling edge of VSync
if(!vsD && vsD2) begin
v_cnt <= 0;
vs_high <= v_cnt;
end
// rising edge of VSync
else if(vsD && !vsD2) begin
v_cnt <= 0;
vs_low <= v_cnt;
end
end
end
// area in which OSD is being displayed
wire [9:0] h_osd_start = ((dsp_width - OSD_WIDTH)>> 1) + OSD_X_OFFSET;
wire [9:0] h_osd_end = h_osd_start + OSD_WIDTH;
wire [9:0] v_osd_start = ((dsp_height- (OSD_HEIGHT<<doublescan))>> 1) + OSD_Y_OFFSET;
wire [9:0] v_osd_end = v_osd_start + (OSD_HEIGHT<<doublescan);
wire [9:0] osd_hcnt = h_cnt - h_osd_start + 1'd1; // one pixel offset for osd_byte register
wire [9:0] osd_vcnt = v_cnt - v_osd_start;
wire osd_de = osd_enable &&
(HSync != hs_pol) && (h_cnt >= h_osd_start) && (h_cnt < h_osd_end) &&
(VSync != vs_pol) && (v_cnt >= v_osd_start) && (v_cnt < v_osd_end);
reg [7:0] osd_byte;
always @(posedge clk_sys) if(ce_pix) osd_byte <= osd_buffer[{doublescan ? osd_vcnt[7:5] : osd_vcnt[6:4], osd_hcnt[7:0]}];
wire osd_pixel = osd_byte[doublescan ? osd_vcnt[4:2] : osd_vcnt[3:1]];
assign R_out = !osd_de ? R_in : {osd_pixel, osd_pixel, OSD_COLOR[2], R_in[5:3]};
assign G_out = !osd_de ? G_in : {osd_pixel, osd_pixel, OSD_COLOR[1], G_in[5:3]};
assign B_out = !osd_de ? B_in : {osd_pixel, osd_pixel, OSD_COLOR[0], B_in[5:3]};
endmodule

View File

@ -0,0 +1,195 @@
//
// scandoubler.v
//
// Copyright (c) 2015 Till Harbaum <till@harbaum.org>
// Copyright (c) 2017 Sorgelig
//
// 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 <http://www.gnu.org/licenses/>.
// TODO: Delay vsync one line
module scandoubler #(parameter LENGTH, parameter HALF_DEPTH)
(
// system interface
input clk_sys,
input ce_pix,
input ce_pix_actual,
input hq2x,
// shifter video interface
input hs_in,
input vs_in,
input line_start,
input [DWIDTH:0] r_in,
input [DWIDTH:0] g_in,
input [DWIDTH:0] b_in,
input mono,
// output interface
output reg hs_out,
output vs_out,
output [DWIDTH:0] r_out,
output [DWIDTH:0] g_out,
output [DWIDTH:0] b_out
);
localparam DWIDTH = HALF_DEPTH ? 2 : 5;
assign vs_out = vs_in;
reg [2:0] phase;
reg [2:0] ce_div;
reg [7:0] pix_len = 0;
wire [7:0] pl = pix_len + 1'b1;
reg ce_x1, ce_x4;
reg req_line_reset;
wire ls_in = hs_in | line_start;
always @(negedge clk_sys) begin
reg old_ce;
reg [2:0] ce_cnt;
reg [7:0] pixsz2, pixsz4 = 0;
old_ce <= ce_pix;
if(~&pix_len) pix_len <= pix_len + 1'd1;
ce_x4 <= 0;
ce_x1 <= 0;
// use such odd comparison to place c_x4 evenly if master clock isn't multiple 4.
if((pl == pixsz4) || (pl == pixsz2) || (pl == (pixsz2+pixsz4))) begin
phase <= phase + 1'd1;
ce_x4 <= 1;
end
if(~old_ce & ce_pix) begin
pixsz2 <= {1'b0, pl[7:1]};
pixsz4 <= {2'b00, pl[7:2]};
ce_x1 <= 1;
ce_x4 <= 1;
pix_len <= 0;
phase <= phase + 1'd1;
ce_cnt <= ce_cnt + 1'd1;
if(ce_pix_actual) begin
phase <= 0;
ce_div <= ce_cnt + 1'd1;
ce_cnt <= 0;
req_line_reset <= 0;
end
if(ls_in) req_line_reset <= 1;
end
end
reg ce_sd;
always @(*) begin
case(ce_div)
2: ce_sd = !phase[0];
4: ce_sd = !phase[1:0];
default: ce_sd <= 1;
endcase
end
`define BITS_TO_FIT(N) ( \
N <= 2 ? 0 : \
N <= 4 ? 1 : \
N <= 8 ? 2 : \
N <= 16 ? 3 : \
N <= 32 ? 4 : \
N <= 64 ? 5 : \
N <= 128 ? 6 : \
N <= 256 ? 7 : \
N <= 512 ? 8 : \
N <=1024 ? 9 : 10 )
localparam AWIDTH = `BITS_TO_FIT(LENGTH);
Hq2x #(.LENGTH(LENGTH), .HALF_DEPTH(HALF_DEPTH)) Hq2x
(
.clk(clk_sys),
.ce_x4(ce_x4 & ce_sd),
.inputpixel({b_in,g_in,r_in}),
.mono(mono),
.disable_hq2x(~hq2x),
.reset_frame(vs_in),
.reset_line(req_line_reset),
.read_y(sd_line),
.read_x(sd_h_actual),
.outpixel({b_out,g_out,r_out})
);
reg [10:0] sd_h_actual;
always @(*) begin
case(ce_div)
2: sd_h_actual = sd_h[10:1];
4: sd_h_actual = sd_h[10:2];
default: sd_h_actual = sd_h;
endcase
end
reg [10:0] sd_h;
reg [1:0] sd_line;
always @(posedge clk_sys) begin
reg [11:0] hs_max,hs_rise,hs_ls;
reg [10:0] hcnt;
reg [11:0] sd_hcnt;
reg hs, hs2, vs, ls;
if(ce_x1) begin
hs <= hs_in;
ls <= ls_in;
if(ls && !ls_in) hs_ls <= {hcnt,1'b1};
// falling edge of hsync indicates start of line
if(hs && !hs_in) begin
hs_max <= {hcnt,1'b1};
hcnt <= 0;
if(ls && !ls_in) hs_ls <= {10'd0,1'b1};
end else begin
hcnt <= hcnt + 1'd1;
end
// save position of rising edge
if(!hs && hs_in) hs_rise <= {hcnt,1'b1};
vs <= vs_in;
if(vs && ~vs_in) sd_line <= 0;
end
if(ce_x4) begin
hs2 <= hs_in;
// output counter synchronous to input and at twice the rate
sd_hcnt <= sd_hcnt + 1'd1;
sd_h <= sd_h + 1'd1;
if(hs2 && !hs_in) sd_hcnt <= hs_max;
if(sd_hcnt == hs_max) sd_hcnt <= 0;
// replicate horizontal sync at twice the speed
if(sd_hcnt == hs_max) hs_out <= 0;
if(sd_hcnt == hs_rise) hs_out <= 1;
if(sd_hcnt == hs_ls) sd_h <= 0;
if(sd_hcnt == hs_ls) sd_line <= sd_line + 1'd1;
end
end
endmodule

View File

@ -0,0 +1,242 @@
//
//
// Copyright (c) 2017 Sorgelig
//
// This program is GPL Licensed. See COPYING for the full license.
//
//
////////////////////////////////////////////////////////////////////////////////////////////////////////
`timescale 1ns / 1ps
//
// LINE_LENGTH: Length of display line in pixels
// Usually it's length from HSync to HSync.
// May be less if line_start is used.
//
// HALF_DEPTH: If =1 then color dept is 3 bits per component
// For half depth 6 bits monochrome is available with
// mono signal enabled and color = {G, R}
module video_mixer
#(
parameter LINE_LENGTH = 768,
parameter HALF_DEPTH = 0,
parameter OSD_COLOR = 3'd4,
parameter OSD_X_OFFSET = 10'd0,
parameter OSD_Y_OFFSET = 10'd0
)
(
// master clock
// it should be multiple by (ce_pix*4).
input clk_sys,
// Pixel clock or clock_enable (both are accepted).
input ce_pix,
// Some systems have multiple resolutions.
// ce_pix_actual should match ce_pix where every second or fourth pulse is enabled,
// thus half or qurter resolutions can be used without brake video sync while switching resolutions.
// For fixed single resolution (or when video sync stability isn't required) ce_pix_actual = ce_pix.
input ce_pix_actual,
// OSD SPI interface
input SPI_SCK,
input SPI_SS3,
input SPI_DI,
// scanlines (00-none 01-25% 10-50% 11-75%)
input [1:0] scanlines,
// 0 = HVSync 31KHz, 1 = CSync 15KHz
input scandoubler_disable,
// High quality 2x scaling
input hq2x,
// YPbPr always uses composite sync
input ypbpr,
// 0 = 16-240 range. 1 = 0-255 range. (only for YPbPr color space)
input ypbpr_full,
// color
input [DWIDTH:0] R,
input [DWIDTH:0] G,
input [DWIDTH:0] B,
// Monochrome mode (for HALF_DEPTH only)
input mono,
// interlace sync. Positive pulses.
input HSync,
input VSync,
// Falling of this signal means start of informative part of line.
// It can be horizontal blank signal.
// This signal can be used to reduce amount of required FPGA RAM for HQ2x scan doubler
// If FPGA RAM is not an issue, then simply set it to 0 for whole line processing.
// Keep in mind: due to algo first and last pixels of line should be black to avoid side artefacts.
// Thus, if blank signal is used to reduce the line, make sure to feed at least one black (or paper) pixel
// before first informative pixel.
input line_start,
// MiST video output signals
output [5:0] VGA_R,
output [5:0] VGA_G,
output [5:0] VGA_B,
output VGA_VS,
output VGA_HS
);
localparam DWIDTH = HALF_DEPTH ? 2 : 5;
wire [DWIDTH:0] R_sd;
wire [DWIDTH:0] G_sd;
wire [DWIDTH:0] B_sd;
wire hs_sd, vs_sd;
scandoubler #(.LENGTH(LINE_LENGTH), .HALF_DEPTH(HALF_DEPTH)) scandoubler
(
.*,
.hs_in(HSync),
.vs_in(VSync),
.r_in(R),
.g_in(G),
.b_in(B),
.hs_out(hs_sd),
.vs_out(vs_sd),
.r_out(R_sd),
.g_out(G_sd),
.b_out(B_sd)
);
wire [DWIDTH:0] rt = (scandoubler_disable ? R : R_sd);
wire [DWIDTH:0] gt = (scandoubler_disable ? G : G_sd);
wire [DWIDTH:0] bt = (scandoubler_disable ? B : B_sd);
generate
if(HALF_DEPTH) begin
wire [5:0] r = mono ? {gt,rt} : {rt,rt};
wire [5:0] g = mono ? {gt,rt} : {gt,gt};
wire [5:0] b = mono ? {gt,rt} : {bt,bt};
end else begin
wire [5:0] r = rt;
wire [5:0] g = gt;
wire [5:0] b = bt;
end
endgenerate
wire hs = (scandoubler_disable ? HSync : hs_sd);
wire vs = (scandoubler_disable ? VSync : vs_sd);
reg scanline = 0;
always @(posedge clk_sys) begin
reg old_hs, old_vs;
old_hs <= hs;
old_vs <= vs;
if(old_hs && ~hs) scanline <= ~scanline;
if(old_vs && ~vs) scanline <= 0;
end
wire [5:0] r_out, g_out, b_out;
always @(*) begin
case(scanlines & {scanline, scanline})
1: begin // reduce 25% = 1/2 + 1/4
r_out = {1'b0, r[5:1]} + {2'b00, r[5:2]};
g_out = {1'b0, g[5:1]} + {2'b00, g[5:2]};
b_out = {1'b0, b[5:1]} + {2'b00, b[5:2]};
end
2: begin // reduce 50% = 1/2
r_out = {1'b0, r[5:1]};
g_out = {1'b0, g[5:1]};
b_out = {1'b0, b[5:1]};
end
3: begin // reduce 75% = 1/4
r_out = {2'b00, r[5:2]};
g_out = {2'b00, g[5:2]};
b_out = {2'b00, b[5:2]};
end
default: begin
r_out = r;
g_out = g;
b_out = b;
end
endcase
end
wire [5:0] red, green, blue;
osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR) osd
(
.*,
.R_in(r_out),
.G_in(g_out),
.B_in(b_out),
.HSync(hs),
.VSync(vs),
.R_out(red),
.G_out(green),
.B_out(blue)
);
wire [5:0] yuv_full[225] = '{
6'd0, 6'd0, 6'd0, 6'd0, 6'd1, 6'd1, 6'd1, 6'd1,
6'd2, 6'd2, 6'd2, 6'd3, 6'd3, 6'd3, 6'd3, 6'd4,
6'd4, 6'd4, 6'd5, 6'd5, 6'd5, 6'd5, 6'd6, 6'd6,
6'd6, 6'd7, 6'd7, 6'd7, 6'd7, 6'd8, 6'd8, 6'd8,
6'd9, 6'd9, 6'd9, 6'd9, 6'd10, 6'd10, 6'd10, 6'd11,
6'd11, 6'd11, 6'd11, 6'd12, 6'd12, 6'd12, 6'd13, 6'd13,
6'd13, 6'd13, 6'd14, 6'd14, 6'd14, 6'd15, 6'd15, 6'd15,
6'd15, 6'd16, 6'd16, 6'd16, 6'd17, 6'd17, 6'd17, 6'd17,
6'd18, 6'd18, 6'd18, 6'd19, 6'd19, 6'd19, 6'd19, 6'd20,
6'd20, 6'd20, 6'd21, 6'd21, 6'd21, 6'd21, 6'd22, 6'd22,
6'd22, 6'd23, 6'd23, 6'd23, 6'd23, 6'd24, 6'd24, 6'd24,
6'd25, 6'd25, 6'd25, 6'd25, 6'd26, 6'd26, 6'd26, 6'd27,
6'd27, 6'd27, 6'd27, 6'd28, 6'd28, 6'd28, 6'd29, 6'd29,
6'd29, 6'd29, 6'd30, 6'd30, 6'd30, 6'd31, 6'd31, 6'd31,
6'd31, 6'd32, 6'd32, 6'd32, 6'd33, 6'd33, 6'd33, 6'd33,
6'd34, 6'd34, 6'd34, 6'd35, 6'd35, 6'd35, 6'd35, 6'd36,
6'd36, 6'd36, 6'd36, 6'd37, 6'd37, 6'd37, 6'd38, 6'd38,
6'd38, 6'd38, 6'd39, 6'd39, 6'd39, 6'd40, 6'd40, 6'd40,
6'd40, 6'd41, 6'd41, 6'd41, 6'd42, 6'd42, 6'd42, 6'd42,
6'd43, 6'd43, 6'd43, 6'd44, 6'd44, 6'd44, 6'd44, 6'd45,
6'd45, 6'd45, 6'd46, 6'd46, 6'd46, 6'd46, 6'd47, 6'd47,
6'd47, 6'd48, 6'd48, 6'd48, 6'd48, 6'd49, 6'd49, 6'd49,
6'd50, 6'd50, 6'd50, 6'd50, 6'd51, 6'd51, 6'd51, 6'd52,
6'd52, 6'd52, 6'd52, 6'd53, 6'd53, 6'd53, 6'd54, 6'd54,
6'd54, 6'd54, 6'd55, 6'd55, 6'd55, 6'd56, 6'd56, 6'd56,
6'd56, 6'd57, 6'd57, 6'd57, 6'd58, 6'd58, 6'd58, 6'd58,
6'd59, 6'd59, 6'd59, 6'd60, 6'd60, 6'd60, 6'd60, 6'd61,
6'd61, 6'd61, 6'd62, 6'd62, 6'd62, 6'd62, 6'd63, 6'd63,
6'd63
};
// http://marsee101.blog19.fc2.com/blog-entry-2311.html
// Y = 16 + 0.257*R + 0.504*G + 0.098*B (Y = 0.299*R + 0.587*G + 0.114*B)
// Pb = 128 - 0.148*R - 0.291*G + 0.439*B (Pb = -0.169*R - 0.331*G + 0.500*B)
// Pr = 128 + 0.439*R - 0.368*G - 0.071*B (Pr = 0.500*R - 0.419*G - 0.081*B)
wire [18:0] y_8 = 19'd04096 + ({red, 8'd0} + {red, 3'd0}) + ({green, 9'd0} + {green, 2'd0}) + ({blue, 6'd0} + {blue, 5'd0} + {blue, 2'd0});
wire [18:0] pb_8 = 19'd32768 - ({red, 7'd0} + {red, 4'd0} + {red, 3'd0}) - ({green, 8'd0} + {green, 5'd0} + {green, 3'd0}) + ({blue, 8'd0} + {blue, 7'd0} + {blue, 6'd0});
wire [18:0] pr_8 = 19'd32768 + ({red, 8'd0} + {red, 7'd0} + {red, 6'd0}) - ({green, 8'd0} + {green, 6'd0} + {green, 5'd0} + {green, 4'd0} + {green, 3'd0}) - ({blue, 6'd0} + {blue , 3'd0});
wire [7:0] y = ( y_8[17:8] < 16) ? 8'd16 : ( y_8[17:8] > 235) ? 8'd235 : y_8[15:8];
wire [7:0] pb = (pb_8[17:8] < 16) ? 8'd16 : (pb_8[17:8] > 240) ? 8'd240 : pb_8[15:8];
wire [7:0] pr = (pr_8[17:8] < 16) ? 8'd16 : (pr_8[17:8] > 240) ? 8'd240 : pr_8[15:8];
assign VGA_R = ypbpr ? (ypbpr_full ? yuv_full[pr-8'd16] : pr[7:2]) : red;
assign VGA_G = ypbpr ? (ypbpr_full ? yuv_full[y -8'd16] : y[7:2]) : green;
assign VGA_B = ypbpr ? (ypbpr_full ? yuv_full[pb-8'd16] : pb[7:2]) : blue;
assign VGA_VS = (scandoubler_disable | ypbpr) ? 1'b1 : ~vs_sd;
assign VGA_HS = scandoubler_disable ? ~(HSync ^ VSync) : ypbpr ? ~(hs_sd ^ vs_sd) : ~hs_sd;
endmodule

32
rtl/old/aholme_6502.v Normal file
View File

@ -0,0 +1,32 @@
module aholme_6502(
input clk,
input enable,
input reset,
output [15:0] ab,
input [7:0] dbi,
output [7:0] dbo,
output we,
input irq,
input nmi,
input ready
);
wire we_c;
chip_6502 aholme_cpu (
.clk(clk),
.phi(clk & enable),
.res(~reset),
.so(1'b0),
.rdy(ready),
.nmi(nmi_n),
.irq(irq_n),
.rw(we_c),
.dbi(dbi),
.dbo(dbo),
.ab(ab)
);
assign we = ~we_c;
endmodule

122
rtl/old/tm1638.v Normal file
View File

@ -0,0 +1,122 @@
module tm1638(
input clk,
input clk_en,
input rst,
input data_latch,
inout [7:0] data,
input rw,
output busy,
output sclk,
input dio_in,
output reg dio_out
);
localparam CLK_DIV = 3; // seems happy at 12MHz with 3
localparam CLK_DIV1 = CLK_DIV - 1;
localparam [1:0]
S_IDLE = 2'h0,
S_WAIT = 2'h1,
S_TRANSFER = 2'h2;
reg [1:0] cur_state, next_state;
reg [CLK_DIV1:0] sclk_d, sclk_q;
reg [7:0] data_d, data_q, data_out_d, data_out_q;
reg dio_out_d;
reg [2:0] ctr_d, ctr_q;
// output read data if we're reading
assign data = rw ? 8'hZZ : data_out_q;
// we're busy if we're not idle
assign busy = cur_state != S_IDLE;
// tick the clock if we're transfering data
assign sclk = ~((~sclk_q[CLK_DIV1]) & (cur_state == S_TRANSFER));
always @(*)
begin
sclk_d = sclk_q;
data_d = data_q;
dio_out_d = dio_out;
ctr_d = ctr_q;
data_out_d = data_out_q;
next_state = cur_state;
case(cur_state)
S_IDLE: begin
sclk_d = 0;
if (data_latch) begin
// if we're reading, set to zero, otherwise latch in
// data to send
data_d = rw ? data : 8'b0;
next_state = S_WAIT;
end
end
S_WAIT: begin
sclk_d = sclk_q + 1;
// wait till we're halfway into clock pulse
if (sclk_q == {1'b0, {CLK_DIV1{1'b1}}}) begin
sclk_d = 0;
next_state = S_TRANSFER;
end
end
S_TRANSFER: begin
sclk_d = sclk_q + 1;
if (sclk_q == 0) begin
// start of clock pulse, output MSB
dio_out_d = data_q[0];
end else if (sclk_q == {1'b0, {CLK_DIV1{1'b1}}}) begin
// halfway through pulse, read from device
data_d = {dio_in, data_q[7:1]};
end else if (&sclk_q) begin
// end of pulse, tick the counter
ctr_d = ctr_q + 1;
if (&ctr_q) begin
// last bit sent, switch back to idle
// and output any data recieved
next_state = S_IDLE;
data_out_d = data_q;
dio_out_d = 0;
end
end
end
default:
next_state = S_IDLE;
endcase
end
always @(posedge clk)
begin
if (clk_en)
begin
if (rst)
begin
cur_state <= S_IDLE;
sclk_q <= 0;
ctr_q <= 0;
dio_out <= 0;
data_q <= 0;
data_out_q <= 0;
end
else
begin
cur_state <= next_state;
sclk_q <= sclk_d;
ctr_q <= ctr_d;
dio_out <= dio_out_d;
data_q <= data_d;
data_out_q <= data_out_d;
end
end
end
endmodule

4
rtl/pll.qip Normal file
View File

@ -0,0 +1,4 @@
set_global_assignment -name IP_TOOL_NAME "ALTPLL"
set_global_assignment -name IP_TOOL_VERSION "13.1"
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "pll.v"]
set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "pll.ppf"]

348
rtl/pll.v Normal file
View File

@ -0,0 +1,348 @@
// megafunction wizard: %ALTPLL%
// GENERATION: STANDARD
// VERSION: WM1.0
// MODULE: altpll
// ============================================================
// File Name: pll.v
// Megafunction Name(s):
// altpll
//
// Simulation Library Files(s):
// altera_mf
// ============================================================
// ************************************************************
// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
//
// 13.1.4 Build 182 03/12/2014 SJ Web Edition
// ************************************************************
//Copyright (C) 1991-2014 Altera Corporation
//Your use of Altera Corporation's design tools, logic functions
//and other software and tools, and its AMPP partner logic
//functions, and any output files from any of the foregoing
//(including device programming or simulation files), and any
//associated documentation or information are expressly subject
//to the terms and conditions of the Altera Program License
//Subscription Agreement, Altera MegaCore Function License
//Agreement, or other applicable license agreement, including,
//without limitation, that your use is for the sole purpose of
//programming logic devices manufactured by Altera and sold by
//Altera or its authorized distributors. Please refer to the
//applicable agreement for further details.
// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module pll (
areset,
inclk0,
c0,
c1,
locked);
input areset;
input inclk0;
output c0;
output c1;
output locked;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
tri0 areset;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif
wire [4:0] sub_wire0;
wire sub_wire2;
wire [0:0] sub_wire6 = 1'h0;
wire [0:0] sub_wire3 = sub_wire0[0:0];
wire [1:1] sub_wire1 = sub_wire0[1:1];
wire c1 = sub_wire1;
wire locked = sub_wire2;
wire c0 = sub_wire3;
wire sub_wire4 = inclk0;
wire [1:0] sub_wire5 = {sub_wire6, sub_wire4};
altpll altpll_component (
.areset (areset),
.inclk (sub_wire5),
.clk (sub_wire0),
.locked (sub_wire2),
.activeclock (),
.clkbad (),
.clkena ({6{1'b1}}),
.clkloss (),
.clkswitch (1'b0),
.configupdate (1'b0),
.enable0 (),
.enable1 (),
.extclk (),
.extclkena ({4{1'b1}}),
.fbin (1'b1),
.fbmimicbidir (),
.fbout (),
.fref (),
.icdrclk (),
.pfdena (1'b1),
.phasecounterselect ({4{1'b1}}),
.phasedone (),
.phasestep (1'b1),
.phaseupdown (1'b1),
.pllena (1'b1),
.scanaclr (1'b0),
.scanclk (1'b0),
.scanclkena (1'b1),
.scandata (1'b0),
.scandataout (),
.scandone (),
.scanread (1'b0),
.scanwrite (1'b0),
.sclkout0 (),
.sclkout1 (),
.vcooverrange (),
.vcounderrange ());
defparam
altpll_component.bandwidth_type = "AUTO",
altpll_component.clk0_divide_by = 675000,
altpll_component.clk0_duty_cycle = 50,
altpll_component.clk0_multiply_by = 715909,
altpll_component.clk0_phase_shift = "0",
altpll_component.clk1_divide_by = 1350000,
altpll_component.clk1_duty_cycle = 50,
altpll_component.clk1_multiply_by = 715909,
altpll_component.clk1_phase_shift = "0",
altpll_component.compensate_clock = "CLK0",
altpll_component.inclk0_input_frequency = 37037,
altpll_component.intended_device_family = "Cyclone III",
altpll_component.lpm_hint = "CBX_MODULE_PREFIX=pll",
altpll_component.lpm_type = "altpll",
altpll_component.operation_mode = "NORMAL",
altpll_component.pll_type = "AUTO",
altpll_component.port_activeclock = "PORT_UNUSED",
altpll_component.port_areset = "PORT_USED",
altpll_component.port_clkbad0 = "PORT_UNUSED",
altpll_component.port_clkbad1 = "PORT_UNUSED",
altpll_component.port_clkloss = "PORT_UNUSED",
altpll_component.port_clkswitch = "PORT_UNUSED",
altpll_component.port_configupdate = "PORT_UNUSED",
altpll_component.port_fbin = "PORT_UNUSED",
altpll_component.port_inclk0 = "PORT_USED",
altpll_component.port_inclk1 = "PORT_UNUSED",
altpll_component.port_locked = "PORT_USED",
altpll_component.port_pfdena = "PORT_UNUSED",
altpll_component.port_phasecounterselect = "PORT_UNUSED",
altpll_component.port_phasedone = "PORT_UNUSED",
altpll_component.port_phasestep = "PORT_UNUSED",
altpll_component.port_phaseupdown = "PORT_UNUSED",
altpll_component.port_pllena = "PORT_UNUSED",
altpll_component.port_scanaclr = "PORT_UNUSED",
altpll_component.port_scanclk = "PORT_UNUSED",
altpll_component.port_scanclkena = "PORT_UNUSED",
altpll_component.port_scandata = "PORT_UNUSED",
altpll_component.port_scandataout = "PORT_UNUSED",
altpll_component.port_scandone = "PORT_UNUSED",
altpll_component.port_scanread = "PORT_UNUSED",
altpll_component.port_scanwrite = "PORT_UNUSED",
altpll_component.port_clk0 = "PORT_USED",
altpll_component.port_clk1 = "PORT_USED",
altpll_component.port_clk2 = "PORT_UNUSED",
altpll_component.port_clk3 = "PORT_UNUSED",
altpll_component.port_clk4 = "PORT_UNUSED",
altpll_component.port_clk5 = "PORT_UNUSED",
altpll_component.port_clkena0 = "PORT_UNUSED",
altpll_component.port_clkena1 = "PORT_UNUSED",
altpll_component.port_clkena2 = "PORT_UNUSED",
altpll_component.port_clkena3 = "PORT_UNUSED",
altpll_component.port_clkena4 = "PORT_UNUSED",
altpll_component.port_clkena5 = "PORT_UNUSED",
altpll_component.port_extclk0 = "PORT_UNUSED",
altpll_component.port_extclk1 = "PORT_UNUSED",
altpll_component.port_extclk2 = "PORT_UNUSED",
altpll_component.port_extclk3 = "PORT_UNUSED",
altpll_component.self_reset_on_loss_lock = "OFF",
altpll_component.width_clock = 5;
endmodule
// ============================================================
// CNX file retrieval info
// ============================================================
// Retrieval info: PRIVATE: ACTIVECLK_CHECK STRING "0"
// Retrieval info: PRIVATE: BANDWIDTH STRING "1.000"
// Retrieval info: PRIVATE: BANDWIDTH_FEATURE_ENABLED STRING "1"
// Retrieval info: PRIVATE: BANDWIDTH_FREQ_UNIT STRING "MHz"
// Retrieval info: PRIVATE: BANDWIDTH_PRESET STRING "Low"
// Retrieval info: PRIVATE: BANDWIDTH_USE_AUTO STRING "1"
// Retrieval info: PRIVATE: BANDWIDTH_USE_PRESET STRING "0"
// Retrieval info: PRIVATE: CLKBAD_SWITCHOVER_CHECK STRING "0"
// Retrieval info: PRIVATE: CLKLOSS_CHECK STRING "0"
// Retrieval info: PRIVATE: CLKSWITCH_CHECK STRING "0"
// Retrieval info: PRIVATE: CNX_NO_COMPENSATE_RADIO STRING "0"
// Retrieval info: PRIVATE: CREATE_CLKBAD_CHECK STRING "0"
// Retrieval info: PRIVATE: CREATE_INCLK1_CHECK STRING "0"
// Retrieval info: PRIVATE: CUR_DEDICATED_CLK STRING "c0"
// Retrieval info: PRIVATE: CUR_FBIN_CLK STRING "c0"
// Retrieval info: PRIVATE: DEVICE_SPEED_GRADE STRING "8"
// Retrieval info: PRIVATE: DIV_FACTOR0 NUMERIC "108"
// Retrieval info: PRIVATE: DIV_FACTOR1 NUMERIC "27"
// Retrieval info: PRIVATE: DUTY_CYCLE0 STRING "50.00000000"
// Retrieval info: PRIVATE: DUTY_CYCLE1 STRING "50.00000000"
// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE0 STRING "28.636360"
// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE1 STRING "14.318180"
// Retrieval info: PRIVATE: EXPLICIT_SWITCHOVER_COUNTER STRING "0"
// Retrieval info: PRIVATE: EXT_FEEDBACK_RADIO STRING "0"
// Retrieval info: PRIVATE: GLOCKED_COUNTER_EDIT_CHANGED STRING "1"
// Retrieval info: PRIVATE: GLOCKED_FEATURE_ENABLED STRING "0"
// Retrieval info: PRIVATE: GLOCKED_MODE_CHECK STRING "0"
// Retrieval info: PRIVATE: GLOCK_COUNTER_EDIT NUMERIC "1048575"
// Retrieval info: PRIVATE: HAS_MANUAL_SWITCHOVER STRING "1"
// Retrieval info: PRIVATE: INCLK0_FREQ_EDIT STRING "27.000"
// Retrieval info: PRIVATE: INCLK0_FREQ_UNIT_COMBO STRING "MHz"
// Retrieval info: PRIVATE: INCLK1_FREQ_EDIT STRING "100.000"
// Retrieval info: PRIVATE: INCLK1_FREQ_EDIT_CHANGED STRING "1"
// Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_CHANGED STRING "1"
// Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_COMBO STRING "MHz"
// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: PRIVATE: INT_FEEDBACK__MODE_RADIO STRING "1"
// Retrieval info: PRIVATE: LOCKED_OUTPUT_CHECK STRING "1"
// Retrieval info: PRIVATE: LONG_SCAN_RADIO STRING "1"
// Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE STRING "Not Available"
// Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE_DIRTY NUMERIC "0"
// Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT0 STRING "deg"
// Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT1 STRING "deg"
// Retrieval info: PRIVATE: MIG_DEVICE_SPEED_GRADE STRING "Any"
// Retrieval info: PRIVATE: MIRROR_CLK0 STRING "0"
// Retrieval info: PRIVATE: MIRROR_CLK1 STRING "0"
// Retrieval info: PRIVATE: MULT_FACTOR0 NUMERIC "25"
// Retrieval info: PRIVATE: MULT_FACTOR1 NUMERIC "25"
// Retrieval info: PRIVATE: NORMAL_MODE_RADIO STRING "1"
// Retrieval info: PRIVATE: OUTPUT_FREQ0 STRING "28.63636000"
// Retrieval info: PRIVATE: OUTPUT_FREQ1 STRING "14.31818000"
// Retrieval info: PRIVATE: OUTPUT_FREQ_MODE0 STRING "1"
// Retrieval info: PRIVATE: OUTPUT_FREQ_MODE1 STRING "1"
// Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT0 STRING "MHz"
// Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT1 STRING "MHz"
// Retrieval info: PRIVATE: PHASE_RECONFIG_FEATURE_ENABLED STRING "1"
// Retrieval info: PRIVATE: PHASE_RECONFIG_INPUTS_CHECK STRING "0"
// Retrieval info: PRIVATE: PHASE_SHIFT0 STRING "0.00000000"
// Retrieval info: PRIVATE: PHASE_SHIFT1 STRING "0.00000000"
// Retrieval info: PRIVATE: PHASE_SHIFT_STEP_ENABLED_CHECK STRING "0"
// Retrieval info: PRIVATE: PHASE_SHIFT_UNIT0 STRING "deg"
// Retrieval info: PRIVATE: PHASE_SHIFT_UNIT1 STRING "deg"
// Retrieval info: PRIVATE: PLL_ADVANCED_PARAM_CHECK STRING "0"
// Retrieval info: PRIVATE: PLL_ARESET_CHECK STRING "1"
// Retrieval info: PRIVATE: PLL_AUTOPLL_CHECK NUMERIC "1"
// Retrieval info: PRIVATE: PLL_ENHPLL_CHECK NUMERIC "0"
// Retrieval info: PRIVATE: PLL_FASTPLL_CHECK NUMERIC "0"
// Retrieval info: PRIVATE: PLL_FBMIMIC_CHECK STRING "0"
// Retrieval info: PRIVATE: PLL_LVDS_PLL_CHECK NUMERIC "0"
// Retrieval info: PRIVATE: PLL_PFDENA_CHECK STRING "0"
// Retrieval info: PRIVATE: PLL_TARGET_HARCOPY_CHECK NUMERIC "0"
// Retrieval info: PRIVATE: PRIMARY_CLK_COMBO STRING "inclk0"
// Retrieval info: PRIVATE: RECONFIG_FILE STRING "pll.mif"
// Retrieval info: PRIVATE: SACN_INPUTS_CHECK STRING "0"
// Retrieval info: PRIVATE: SCAN_FEATURE_ENABLED STRING "1"
// Retrieval info: PRIVATE: SELF_RESET_LOCK_LOSS STRING "0"
// Retrieval info: PRIVATE: SHORT_SCAN_RADIO STRING "0"
// Retrieval info: PRIVATE: SPREAD_FEATURE_ENABLED STRING "0"
// Retrieval info: PRIVATE: SPREAD_FREQ STRING "50.000"
// Retrieval info: PRIVATE: SPREAD_FREQ_UNIT STRING "KHz"
// Retrieval info: PRIVATE: SPREAD_PERCENT STRING "0.500"
// Retrieval info: PRIVATE: SPREAD_USE STRING "0"
// Retrieval info: PRIVATE: SRC_SYNCH_COMP_RADIO STRING "0"
// Retrieval info: PRIVATE: STICKY_CLK0 STRING "1"
// Retrieval info: PRIVATE: STICKY_CLK1 STRING "1"
// Retrieval info: PRIVATE: SWITCHOVER_COUNT_EDIT NUMERIC "1"
// Retrieval info: PRIVATE: SWITCHOVER_FEATURE_ENABLED STRING "1"
// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
// Retrieval info: PRIVATE: USE_CLK0 STRING "1"
// Retrieval info: PRIVATE: USE_CLK1 STRING "1"
// Retrieval info: PRIVATE: USE_CLKENA0 STRING "0"
// Retrieval info: PRIVATE: USE_CLKENA1 STRING "0"
// Retrieval info: PRIVATE: USE_MIL_SPEED_GRADE NUMERIC "0"
// Retrieval info: PRIVATE: ZERO_DELAY_RADIO STRING "0"
// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
// Retrieval info: CONSTANT: BANDWIDTH_TYPE STRING "AUTO"
// Retrieval info: CONSTANT: CLK0_DIVIDE_BY NUMERIC "675000"
// Retrieval info: CONSTANT: CLK0_DUTY_CYCLE NUMERIC "50"
// Retrieval info: CONSTANT: CLK0_MULTIPLY_BY NUMERIC "715909"
// Retrieval info: CONSTANT: CLK0_PHASE_SHIFT STRING "0"
// Retrieval info: CONSTANT: CLK1_DIVIDE_BY NUMERIC "1350000"
// Retrieval info: CONSTANT: CLK1_DUTY_CYCLE NUMERIC "50"
// Retrieval info: CONSTANT: CLK1_MULTIPLY_BY NUMERIC "715909"
// Retrieval info: CONSTANT: CLK1_PHASE_SHIFT STRING "0"
// Retrieval info: CONSTANT: COMPENSATE_CLOCK STRING "CLK0"
// Retrieval info: CONSTANT: INCLK0_INPUT_FREQUENCY NUMERIC "37037"
// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: CONSTANT: LPM_TYPE STRING "altpll"
// Retrieval info: CONSTANT: OPERATION_MODE STRING "NORMAL"
// Retrieval info: CONSTANT: PLL_TYPE STRING "AUTO"
// Retrieval info: CONSTANT: PORT_ACTIVECLOCK STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_ARESET STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_CLKBAD0 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_CLKBAD1 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_CLKLOSS STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_CLKSWITCH STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_CONFIGUPDATE STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_FBIN STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_INCLK0 STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_INCLK1 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_LOCKED STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_PFDENA STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PHASECOUNTERSELECT STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PHASEDONE STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PHASESTEP STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PHASEUPDOWN STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PLLENA STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANACLR STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANCLK STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANCLKENA STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANDATA STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANDATAOUT STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANDONE STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANREAD STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANWRITE STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clk0 STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_clk1 STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_clk2 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clk3 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clk4 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clk5 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena0 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena1 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena2 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena3 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena4 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena5 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_extclk0 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_extclk1 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_extclk2 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_extclk3 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: SELF_RESET_ON_LOSS_LOCK STRING "OFF"
// Retrieval info: CONSTANT: WIDTH_CLOCK NUMERIC "5"
// Retrieval info: USED_PORT: @clk 0 0 5 0 OUTPUT_CLK_EXT VCC "@clk[4..0]"
// Retrieval info: USED_PORT: areset 0 0 0 0 INPUT GND "areset"
// Retrieval info: USED_PORT: c0 0 0 0 0 OUTPUT_CLK_EXT VCC "c0"
// Retrieval info: USED_PORT: c1 0 0 0 0 OUTPUT_CLK_EXT VCC "c1"
// Retrieval info: USED_PORT: inclk0 0 0 0 0 INPUT_CLK_EXT GND "inclk0"
// Retrieval info: USED_PORT: locked 0 0 0 0 OUTPUT GND "locked"
// Retrieval info: CONNECT: @areset 0 0 0 0 areset 0 0 0 0
// Retrieval info: CONNECT: @inclk 0 0 1 1 GND 0 0 0 0
// Retrieval info: CONNECT: @inclk 0 0 1 0 inclk0 0 0 0 0
// Retrieval info: CONNECT: c0 0 0 0 0 @clk 0 0 1 0
// Retrieval info: CONNECT: c1 0 0 0 0 @clk 0 0 1 1
// Retrieval info: CONNECT: locked 0 0 0 0 @locked 0 0 0 0
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.v TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.ppf TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.inc FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.cmp FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.bsf FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll_inst.v FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll_bb.v FALSE
// Retrieval info: LIB_FILE: altera_mf
// Retrieval info: CBX_MODULE_PREFIX: ON

339
rtl/ps2keyboard.v Normal file
View File

@ -0,0 +1,339 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: PS/2 keyboard interface
//
// Author.....: Niels A. Moseley
// Date.......: 28-1-2018
//
module ps2keyboard (
input clk14, // 25MHz clock
input rst, // active high reset
// I/O interface to keyboard
input key_clk, // clock input from keyboard / device
input key_din, // data input from keyboard / device
// I/O interface to computer
input cs, // chip select, active high
input address, // =0 RX buffer, =1 RX status
output reg [7:0] dout // 8-bit output bus.
);
reg [3:0] rxcnt; // count how many bits have been shift into rxshiftbuf
reg [10:0] rxshiftbuf; // 11 bit shift receive register
reg rx_flag = 0; // this flag is 1 when
// valid data is available in rxshiftbuf
reg [7:0] rx; // scancode receive buffer
// wire ps2_clkdb; // debounced PS/2 clock signal
reg prev_ps2_clkdb; // previous clock state (in clk14 domain)
// keyboard translation signals
reg [7:0] ascii; // ASCII code of received character
reg ascii_rdy; // new ASCII character received
reg shift; // state of the shift key
reg [2:0] cur_state;
reg [2:0] next_state;
// debounce ps2clk_debounce
// (
// .clk14(clk14),
// .rst(rst),
// .sig_in(key_clk),
// .sig_out(ps2_clkdb)
// );
always @(posedge clk14 or posedge rst)
begin
if (rst)
begin
prev_ps2_clkdb <= 1'b0;
rx_flag <= 1'b0;
end
else
begin
rx_flag <= 1'b0; // reset the new data flag register
// check for negative edge of PS/2 clock
// and sample the state of the PS/2 data line
if ((prev_ps2_clkdb == 1'b1) && (key_clk == 1'b0))//(ps2_clkdb == 1'b0))
begin
rxshiftbuf <= {key_din, rxshiftbuf[10:1]};
rxcnt <= rxcnt + 4'b1;
if (rxcnt == 4'd10)
begin
// 10 bits have been shifted in
// we should have a complete
// scan code here, including
// start, parity and stop bits.
rxcnt <= 0;
// signal new data is present
// note: this signal will only remain high for one
// clock cycle!
//
// TODO: check parity here?
rx_flag <= 1'b1;
end
end
// update previous clock state
prev_ps2_clkdb <= key_clk;//ps2_clkdb;
end
end
//
// IBM Keyboard code page translation
// state machine for US keyboard layout
//
// http://www.computer-engineering.org/ps2keyboard/scancodes2.html
//
localparam S_KEYNORMAL = 3'b000;
localparam S_KEYF0 = 3'b001; // regular key release state
localparam S_KEYE0 = 3'b010; // extended key state
localparam S_KEYE0F0 = 3'b011; // extended release state
always @(posedge clk14 or posedge rst)
begin
if (rst)
begin
rx <= 0;
ascii_rdy <= 0;
shift <= 0;
cur_state <= S_KEYNORMAL;
end
else
begin
// handle I/O from CPU
if (cs == 1'b1)
begin
if (address == 1'b0)
begin
// RX buffer address
dout <= {1'b1, ascii[6:0]};
ascii_rdy <= 1'b0;
end
else
begin
// RX status register
dout <= {ascii_rdy, 7'b0};
end
end
// keyboard translation state machine
if (rx_flag == 1'b1)
begin
// latch data from the serial buffer into
// the rx scancode buffer.
rx <= rxshiftbuf[8:1];
case(cur_state)
S_KEYNORMAL:
begin
if (rx == 8'hF0)
next_state = S_KEYF0;
else if (rx == 8'hE0)
next_state = S_KEYE0;
else
begin
// check the debounce timer, if this is
// not zero, a new key arrived too quickly
// and we simply discard it. For better
// debouncing, we should check if the key
// is actually the same as the previous received/
// key, but let's try this first to see if it works
// ok...
//if (debounce_timer == 16'd0)
//begin
ascii_rdy <= 1'b1; // new key has arrived!
//debounce_timer <= 16'hFFFF; // reset the debounce timer
//end
// check for a SHIFT key
if ((rx == 8'h59) || (rx == 8'h12))
begin
shift <= 1'b1;
ascii_rdy <= 1'b0; // shift is not a key!
end
else begin
if (!shift)
case(rx)
8'h1C: ascii <= "A";
8'h32: ascii <= "B";
8'h21: ascii <= "C";
8'h23: ascii <= "D";
8'h24: ascii <= "E";
8'h2B: ascii <= "F";
8'h34: ascii <= "G";
8'h33: ascii <= "H";
8'h43: ascii <= "I";
8'h3B: ascii <= "J";
8'h42: ascii <= "K";
8'h4B: ascii <= "L";
8'h3A: ascii <= "M";
8'h31: ascii <= "N";
8'h44: ascii <= "O";
8'h4D: ascii <= "P";
8'h15: ascii <= "Q";
8'h2D: ascii <= "R";
8'h1B: ascii <= "S";
8'h2C: ascii <= "T";
8'h3C: ascii <= "U";
8'h2A: ascii <= "V";
8'h1D: ascii <= "W";
8'h22: ascii <= "X";
8'h35: ascii <= "Y";
8'h1A: ascii <= "Z";
8'h45: ascii <= "0";
8'h16: ascii <= "1";
8'h1E: ascii <= "2";
8'h26: ascii <= "3";
8'h25: ascii <= "4";
8'h2E: ascii <= "5";
8'h36: ascii <= "6";
8'h3D: ascii <= "7";
8'h3E: ascii <= "8";
8'h46: ascii <= "9";
8'h4E: ascii <= "-";
8'h55: ascii <= "=";
8'h5D: ascii <= 8'h34; // backslash
8'h66: ascii <= 8'd8; // backspace
8'h29: ascii <= " ";
8'h5A: ascii <= 8'd13; // enter
8'h54: ascii <= "[";
8'h5B: ascii <= "]";
8'h4C: ascii <= ";";
8'h52: ascii <= "'";
8'h41: ascii <= ",";
8'h49: ascii <= ".";
8'h4A: ascii <= "/";
default:
// unsupported key!
begin
ascii_rdy <= 1'b0; // shift is not a key!
ascii <= " ";
end
endcase
else
// Here, we're in a shifted state
case(rx)
8'h1C: ascii <= "A";
8'h32: ascii <= "B";
8'h21: ascii <= "C";
8'h23: ascii <= "D";
8'h24: ascii <= "E";
8'h2B: ascii <= "F";
8'h34: ascii <= "G";
8'h33: ascii <= "H";
8'h43: ascii <= "I";
8'h3B: ascii <= "J";
8'h42: ascii <= "K";
8'h4B: ascii <= "L";
8'h3A: ascii <= "M";
8'h31: ascii <= "N";
8'h44: ascii <= "O";
8'h4D: ascii <= "P";
8'h15: ascii <= "Q";
8'h2D: ascii <= "R";
8'h1B: ascii <= "S";
8'h2C: ascii <= "T";
8'h3C: ascii <= "U";
8'h2A: ascii <= "V";
8'h1D: ascii <= "W";
8'h22: ascii <= "X";
8'h35: ascii <= "Y";
8'h1A: ascii <= "Z";
8'h45: ascii <= ")";
8'h16: ascii <= "!";
8'h1E: ascii <= "@";
8'h26: ascii <= "#";
8'h25: ascii <= "$";
8'h2E: ascii <= "%";
8'h36: ascii <= "^";
8'h3D: ascii <= "&";
8'h3E: ascii <= "*";
8'h46: ascii <= "(";
8'h4E: ascii <= "_";
8'h55: ascii <= "+";
8'h5D: ascii <= "|";
8'h66: ascii <= 8'd8; // backspace
8'h29: ascii <= " ";
8'h5A: ascii <= 8'd13; // enter
8'h54: ascii <= "{";
8'h5B: ascii <= "}";
8'h4C: ascii <= ":";
8'h52: ascii <= "\"";
8'h41: ascii <= "<";
8'h49: ascii <= ">";
8'h4A: ascii <= "?";
default:
// unsupported key!
begin
ascii_rdy <= 1'b0; // shift is not a key!
ascii <= " ";
end
endcase
end
end
end
S_KEYF0:
// when we end up here, a 0xF0 byte was received
// which usually means a key release event
begin
if ((rx == 8'h59) || (rx == 8'h12))
shift <= 1'b0;
next_state = S_KEYNORMAL;
end
S_KEYE0:
begin
if (rx == 8'hF0)
next_state = S_KEYE0F0;
else
next_state = S_KEYNORMAL;
end
S_KEYE0F0:
begin
next_state = S_KEYNORMAL;
end
default:
begin
next_state = S_KEYNORMAL;
end
endcase
end
else
begin
next_state = cur_state; // deliberate blocking assingment!
end
cur_state <= next_state;
end
end
endmodule

55
rtl/pwr_reset.v Normal file
View File

@ -0,0 +1,55 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: Clock divider to provide clock enables for
// devices.
//
// Author.....: Alan Garfield
// Niels A. Moseley
// Date.......: 29-1-2018
//
module pwr_reset(
input clk14, // 14Mhz master clock
input rst_n, // active low synchronous reset
input enable, // clock enable
output rst // active high synchronous system reset
);
reg hard_reset;
reg [5:0] reset_cnt;
wire pwr_up_flag = &reset_cnt;
always @(posedge clk14)
begin
if (rst_n == 1'b0)
begin
reset_cnt <= 6'b0;
hard_reset <= 1'b0;
end
else if (enable)
begin
if (!pwr_up_flag)
reset_cnt <= reset_cnt + 6'b1;
hard_reset <= pwr_up_flag;
end
end
assign rst = ~hard_reset;
endmodule

45
rtl/ram.v Normal file
View File

@ -0,0 +1,45 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: 8KB RAM for system
//
// Author.....: Alan Garfield
// Niels A. Moseley
// Date.......: 26-1-2018
//
module ram (
input clk, // clock signal
input [12:0] address, // address bus
input w_en, // active high write enable strobe
input [7:0] din, // 8-bit data bus (input)
output reg [7:0] dout // 8-bit data bus (output)
);
reg [7:0] ram_data[0:8191];
initial
$readmemh("roms/ram.hex", ram_data, 0, 8191);
always @(posedge clk)
begin
dout <= ram_data[address];
if (w_en) ram_data[address] <= din;
end
endmodule

39
rtl/rom_basic.v Normal file
View File

@ -0,0 +1,39 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: Wrapper for Apple Integer Basic ROM
//
// Author.....: Alan Garfield
// Niels A. Moseley
// Date.......: 26-1-2018
//
module rom_basic (
input clk, // clock signal
input [11:0] address, // address bus
output reg [7:0] dout // 8-bit data bus (output)
);
reg [7:0] rom_data[0:4095];
initial
$readmemh("roms/basic.hex", rom_data, 0, 4095);
always @(posedge clk)
dout <= rom_data[address];
endmodule

41
rtl/rom_wozmon.v Normal file
View File

@ -0,0 +1,41 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: Wrapper for the Woz Mon ROM
//
// Author.....: Alan Garfield
// Niels A. Moseley
// Date.......: 26-1-2018
//
module rom_wozmon (
input clk, // clock signal
input [7:0] address, // address bus
output reg [7:0] dout // 8-bit data bus (output)
);
reg [7:0] rom_data[0:255];
initial
$readmemh("roms/wozmon.hex", rom_data, 0, 255);
always @(posedge clk)
dout <= rom_data[address];
endmodule

4096
rtl/roms/basic.hex Normal file

File diff suppressed because it is too large Load Diff

8192
rtl/roms/ram.hex Normal file

File diff suppressed because it is too large Load Diff

703
rtl/roms/vga_font.bin Normal file
View File

@ -0,0 +1,703 @@
00000000
00000000
00111000
01000100
01010100
01011100
01011000
01000000
00111100
00000000
00000000
00000000
00010000
00101000
01000100
01000100
01111100
01000100
01000100
00000000
00000000
00000000
01111000
01000100
01000100
01111000
01000100
01000100
01111000
00000000
00000000
00000000
00111000
01000100
01000000
01000000
01000000
01000100
00111000
00000000
00000000
00000000
01111000
01000100
01000100
01000100
01000100
01000100
01111000
00000000
00000000
00000000
01111100
01000000
01000000
01111000
01000000
01000000
01111100
00000000
00000000
00000000
01111100
01000000
01000000
01111000
01000000
01000000
01000000
00000000
00000000
00000000
00111100
01000000
01000000
01000000
01001100
01000100
00111100
00000000
00000000
00000000
01000100
01000100
01000100
01111100
01000100
01000100
01000100
00000000
00000000
00000000
00111000
00010000
00010000
00010000
00010000
00010000
00111000
00000000
00000000
00000000
00000100
00000100
00000100
00000100
00000100
01000100
00111000
00000000
00000000
00000000
01000100
01001000
01010000
01100000
01010000
01001000
01000100
00000000
00000000
00000000
01000000
01000000
01000000
01000000
01000000
01000000
01111100
00000000
00000000
00000000
01000100
01101100
01010100
01010100
01000100
01000100
01000100
00000000
00000000
00000000
01000100
01000100
01100100
01010100
01001100
01000100
01000100
00000000
00000000
00000000
00111000
01000100
01000100
01000100
01000100
01000100
00111000
00000000
00000000
00000000
01111000
01000100
01000100
01111000
01000000
01000000
01000000
00000000
00000000
00000000
00111000
01000100
01000100
01000100
01010100
01001000
00110100
00000000
00000000
00000000
01111000
01000100
01000100
01111000
01010000
01001000
01000100
00000000
00000000
00000000
00111000
01000100
01000000
00111000
00000100
01000100
00111000
00000000
00000000
00000000
01111100
00010000
00010000
00010000
00010000
00010000
00010000
00000000
00000000
00000000
01000100
01000100
01000100
01000100
01000100
01000100
00111000
00000000
00000000
00000000
01000100
01000100
01000100
01000100
01000100
00101000
00010000
00000000
00000000
00000000
01000100
01000100
01000100
01010100
01010100
01101100
01000100
00000000
00000000
00000000
01000100
01000100
00101000
00010000
00101000
01000100
01000100
00000000
00000000
00000000
01000100
01000100
00101000
00010000
00010000
00010000
00010000
00000000
00000000
00000000
01111100
00000100
00001000
00010000
00100000
01000000
01111100
00000000
00000000
00000000
01111100
01100000
01100000
01100000
01100000
01100000
01111100
00000000
00000000
00000000
00000000
01000000
00100000
00010000
00001000
00000100
00000000
00000000
00000000
00000000
01111100
00001100
00001100
00001100
00001100
00001100
01111100
00000000
00000000
00000000
00000000
00000000
00010000
00101000
01000100
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
01111100
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00010000
00010000
00010000
00010000
00010000
00000000
00010000
00000000
00000000
00000000
00101000
00101000
00101000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00101000
00101000
01111100
00101000
01111100
00101000
00101000
00000000
00000000
00000000
00010000
00111100
01010000
00111000
00010100
01111000
00010000
00000000
00000000
00000000
01100000
01100100
00001000
00010000
00100000
01001100
00001100
00000000
00000000
00000000
00100000
01010000
01010000
00100000
01010100
01001000
00110100
00000000
00000000
00000000
00010000
00010000
00010000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00010000
00100000
01000000
01000000
01000000
00100000
00010000
00000000
00000000
00000000
00010000
00001000
00000100
00000100
00000100
00001000
00010000
00000000
00000000
00000000
00010000
01010100
00111000
00010000
00111000
01010100
00010000
00000000
00000000
00000000
00000000
00010000
00010000
01111100
00010000
00010000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00010000
00010000
00100000
00000000
00000000
00000000
00000000
00000000
00000000
01111100
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00010000
00000000
00000000
00000000
00000000
00000100
00001000
00010000
00100000
01000000
00000000
00000000
00000000
00000000
00111000
01000100
01001100
01010100
01100100
01000100
00111000
00000000
00000000
00000000
00010000
00110000
00010000
00010000
00010000
00010000
01111100
00000000
00000000
00000000
00111000
01000100
00000100
00011000
00100000
01000000
01111100
00000000
00000000
00000000
01111100
00000100
00001000
00011000
00000100
01000100
00111000
00000000
00000000
00000000
00001000
00011000
00101000
01001000
01111100
00001000
00001000
00000000
00000000
00000000
01111100
01000000
01111000
00000100
00000100
01000100
00111000
00000000
00000000
00000000
00011100
00100000
01000000
01111000
01000100
01000100
00111000
00000000
00000000
00000000
01111100
00000100
00001000
00010000
00100000
00100000
00100000
00000000
00000000
00000000
00111000
01000100
01000100
00111000
01000100
01000100
00111000
00000000
00000000
00000000
00111000
01000100
01000100
00111100
00000100
00001000
01110000
00000000
00000000
00000000
00000000
00000000
00010000
00000000
00010000
00000000
00000000
00000000
00000000
00000000
00000000
00000000
00010000
00000000
00010000
00010000
00100000
00000000
00000000
00000000
00001000
00010000
00100000
01000000
00100000
00010000
00001000
00000000
00000000
00000000
00000000
00000000
01111100
00000000
01111100
00000000
00000000
00000000
00000000
00000000
00100000
00010000
00001000
00000100
00001000
00010000
00100000
00000000
00000000
00000000
00111000
01000100
00001000
00010000
00010000
00000000
00010000
00000000

640
rtl/roms/vga_font.hex Normal file
View File

@ -0,0 +1,640 @@
00
00
38
44
54
5C
58
40
3C
00
00
00
10
28
44
44
7C
44
44
00
00
00
78
44
44
78
44
44
78
00
00
00
38
44
40
40
40
44
38
00
00
00
78
44
44
44
44
44
78
00
00
00
7C
40
40
78
40
40
7C
00
00
00
7C
40
40
78
40
40
40
00
00
00
3C
40
40
40
4C
44
3C
00
00
00
44
44
44
7C
44
44
44
00
00
00
38
10
10
10
10
10
38
00
00
00
04
04
04
04
04
44
38
00
00
00
44
48
50
60
50
48
44
00
00
00
40
40
40
40
40
40
7C
00
00
00
44
6C
54
54
44
44
44
00
00
00
44
44
64
54
4C
44
44
00
00
00
38
44
44
44
44
44
38
00
00
00
78
44
44
78
40
40
40
00
00
00
38
44
44
44
54
48
34
00
00
00
78
44
44
78
50
48
44
00
00
00
38
44
40
38
04
44
38
00
00
00
7C
10
10
10
10
10
10
00
00
00
44
44
44
44
44
44
38
00
00
00
44
44
44
44
44
28
10
00
00
00
44
44
44
54
54
6C
44
00
00
00
44
44
28
10
28
44
44
00
00
00
44
44
28
10
10
10
10
00
00
00
7C
04
08
10
20
40
7C
00
00
00
7C
60
60
60
60
60
7C
00
00
00
00
40
20
10
08
04
00
00
00
00
7C
0C
0C
0C
0C
0C
7C
00
00
00
00
00
10
28
44
00
00
00
00
00
00
00
00
00
00
00
7C
00
00
00
00
00
00
00
00
00
00
00
00
00
10
10
10
10
10
00
10
00
00
00
28
28
28
00
00
00
00
00
00
00
28
28
7C
28
7C
28
28
00
00
00
10
3C
50
38
14
78
10
00
00
00
60
64
08
10
20
4C
0C
00
00
00
20
50
50
20
54
48
34
00
00
00
10
10
10
00
00
00
00
00
00
00
10
20
40
40
40
20
10
00
00
00
10
08
04
04
04
08
10
00
00
00
10
54
38
10
38
54
10
00
00
00
00
10
10
7C
10
10
00
00
00
00
00
00
00
00
10
10
20
00
00
00
00
00
00
7C
00
00
00
00
00
00
00
00
00
00
00
00
10
00
00
00
00
04
08
10
20
40
00
00
00
00
38
44
4C
54
64
44
38
00
00
00
10
30
10
10
10
10
7C
00
00
00
38
44
04
18
20
40
7C
00
00
00
7C
04
08
18
04
44
38
00
00
00
08
18
28
48
7C
08
08
00
00
00
7C
40
78
04
04
44
38
00
00
00
1C
20
40
78
44
44
38
00
00
00
7C
04
08
10
20
20
20
00
00
00
38
44
44
38
44
44
38
00
00
00
38
44
44
3C
04
08
70
00
00
00
00
00
10
00
10
00
00
00
00
00
00
00
10
00
10
10
20
00
00
00
08
10
20
40
20
10
08
00
00
00
00
00
7C
00
7C
00
00
00
00
00
20
10
08
04
08
10
20
00
00
00
38
44
08
10
10
00
10
00

File diff suppressed because it is too large Load Diff

2048
rtl/roms/vga_vram.bin Normal file

File diff suppressed because it is too large Load Diff

256
rtl/roms/wozmon.hex Normal file
View File

@ -0,0 +1,256 @@
D8
58
A0
7F
8C
12
D0
A9
A7
8D
11
D0
8D
13
D0
C9
DF
F0
13
C9
9B
F0
03
C8
10
0F
A9
DC
20
EF
FF
A9
8D
20
EF
FF
A0
01
88
30
F6
AD
11
D0
10
FB
AD
10
D0
99
00
02
20
EF
FF
C9
8D
D0
D4
A0
FF
A9
00
AA
0A
85
2B
C8
B9
00
02
C9
8D
F0
D4
C9
AE
90
F4
F0
F0
C9
BA
F0
EB
C9
D2
F0
3B
86
28
86
29
84
2A
B9
00
02
49
B0
C9
0A
90
06
69
88
C9
FA
90
11
0A
0A
0A
0A
A2
04
0A
26
28
26
29
CA
D0
F8
C8
D0
E0
C4
2A
F0
97
24
2B
50
10
A5
28
81
26
E6
26
D0
B5
E6
27
4C
44
FF
6C
24
00
30
2B
A2
02
B5
27
95
25
95
23
CA
D0
F7
D0
14
A9
8D
20
EF
FF
A5
25
20
DC
FF
A5
24
20
DC
FF
A9
BA
20
EF
FF
A9
A0
20
EF
FF
A1
24
20
DC
FF
86
2B
A5
24
C5
28
A5
25
E5
29
B0
C1
E6
24
D0
02
E6
25
A5
24
29
07
10
C8
48
4A
4A
4A
4A
20
E5
FF
68
29
0F
09
B0
C9
BA
90
02
69
06
2C
12
D0
30
FB
8D
12
D0
60
00
00
00
0F
00
FF
00
00

169
rtl/uart.v Normal file
View File

@ -0,0 +1,169 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: A wrapper for the basic UART from fpga4fun.com
//
// Author.....: Alan Garfield
// Niels A. Moseley
// Date.......: 26-1-2018
//
module uart(
input clk, // clock signal
input enable, // clock enable strobe
input rst, // active high reset signal
input [1:0] address, // address bus
input w_en, // active high write enable strobe
input [7:0] din, // 8-bit data bus (input)
output reg [7:0] dout, // 8-bit data bus (output)
input uart_rx, // asynchronous serial data input from computer
output uart_tx, // asynchronous serial data output to computer
output uart_cts // clear to send flag to computer
);
parameter ClkFrequency = 25000000; // 25MHz
parameter Baud = 115200;
parameter Oversampling = 16;
reg uart_tx_stb, uart_tx_init;
reg [7:0] uart_tx_byte;
wire uart_tx_status;
async_transmitter #(ClkFrequency, Baud) my_tx (
.clk(clk),
.rst(rst),
.TxD_start(uart_tx_stb),
.TxD_data(uart_tx_byte),
.TxD(uart_tx),
.TxD_busy(uart_tx_status)
);
wire uart_rx_stb, rx_idle, rx_end;
wire [7:0] rx_data;
reg uart_rx_status, uart_rx_ack;
reg [7:0] uart_rx_byte;
async_receiver #(ClkFrequency, Baud, Oversampling) my_rx(
.clk(clk),
.rst(rst),
.RxD(uart_rx),
.RxD_data_ready(uart_rx_stb),
.RxD_data(rx_data),
.RxD_idle(rx_idle),
.RxD_endofpacket(rx_end)
);
always @(posedge clk or posedge rst)
begin
if (rst)
begin
uart_rx_status <= 'b0;
uart_rx_byte <= 8'd0;
end
else
begin
// new byte from RX, check register is clear and CPU has seen
// previous byte, otherwise we ignore the new data
if (uart_rx_stb && ~uart_rx_status)
begin
uart_rx_status <= 'b1;
uart_rx_byte <= rx_data;
end
// clear the rx status flag on ack from CPU
if (uart_rx_ack)
uart_rx_status <= 'b0;
end
end
assign uart_cts = ~rx_idle || uart_rx_status;
localparam UART_RX = 2'b00;
localparam UART_RXCR = 2'b01;
localparam UART_TX = 2'b10;
localparam UART_TXCR = 2'b11;
// Handle Register
always @(posedge clk or posedge rst)
begin
if (rst)
begin
dout <= 8'd0;
uart_tx_init <= 0; // flag to ignore the DDR setup from Wozmon PIA call
uart_tx_stb <= 0;
uart_tx_byte <= 8'd0;
uart_rx_ack <= 0;
end
else
begin
uart_tx_stb <= 0;
uart_rx_ack <= 0;
case (address)
UART_RX:
begin
// UART RX - 0xD010
// Bit b7 of KBD is permanently tied to high
dout <= {1'b1, uart_rx_byte[6:0]};
if (~w_en && ~uart_rx_ack && uart_rx_status && enable)
uart_rx_ack <= 1'b1;
end
UART_RXCR:
begin
// UART RX CR - 0xD011
dout <= {uart_rx_status, 7'b0};
end
UART_TX:
begin
// UART TX - 0xD012
dout <= {uart_tx_status, 7'd0};
if (w_en)
begin
// Apple 1 terminal only uses 7 bits, MSB indicates
// terminal has ack'd RX
//
// uart_tx_init is a flag to stop the first character
// sent to the UART from being sent. Wozmon initializes
// the PIA which normally isn't sent to the terminal.
// This causes the UART to ignore the very first byte sent.
if (~uart_tx_status && uart_tx_init)
begin
uart_tx_byte <= {1'b0, din[6:0]};
uart_tx_stb <= 1;
end
else if (~uart_tx_init)
uart_tx_init <= 1 && enable;
end
end
UART_TXCR:
begin
// UART TX CR - 0xD013
// Ignore the TX control register
dout <= 8'b0;
end
endcase
end
end
endmodule

299
rtl/vga.v Normal file
View File

@ -0,0 +1,299 @@
module vga (
input clk14, // clock signal
input enable, // clock enable strobe,
input rst, // active high reset signal
output vga_h_sync, // horizontal VGA sync pulse
output vga_v_sync, // vertical VGA sync pulse
output vga_red, // red VGA signal
output vga_grn, // green VGA signal
output vga_blu, // blue VGA signal
input address, // address bus
input w_en, // active high write enable strobe
input [7:0] din, // 8-bit data bus (input)
input [1:0] mode, // 2-bit mode setting for pixel doubling
input [2:0] fg_colour, // 3 bit background colour
input [2:0] bg_colour, // 3 bit foreground colour
input clr_screen // clear screen button
);
//////////////////////////////////////////////////////////////////////////
// Registers and Parameters
// video structure constants
parameter h_pixels = 910; // horizontal pixels per line
parameter v_lines = 262; // vertical lines per frame
parameter h_pulse = 65; // hsync pulse length (was: 96)
parameter v_pulse = 2; // vsync pulse length
parameter hbp = 144+64; // end of horizontal back porch
parameter hfp = 784+64; // beginning of horizontal front porch
parameter vbp = 10+32; // end of vertical back porch
parameter vfp = 202+32; // beginning of vertical front porch
// registers for storing the horizontal & vertical counters
reg [9:0] h_cnt;
reg [9:0] v_cnt;
wire [3:0] h_dot;
reg [4:0] v_dot;
// hardware cursor registers
wire [10:0] cursor;
reg [5:0] h_cursor;
reg [4:0] v_cursor;
// vram indexing registers
reg [5:0] vram_h_addr;
reg [4:0] vram_v_addr;
reg [4:0] vram_start_addr;
reg [4:0] vram_end_addr;
wire [4:0] vram_clr_addr;
// vram registers
wire [10:0] vram_r_addr;
reg [10:0] vram_w_addr;
reg vram_w_en;
reg [5:0] vram_din;
wire [5:0] vram_dout;
// font rom registers
wire [5:0] font_char;
wire [3:0] font_pixel;
wire [4:0] font_line;
wire font_out;
// cpu control registers
reg char_seen;
// active region strobes
wire h_active;
wire v_active;
assign h_active = (h_cnt >= hbp && h_cnt < hfp);
assign v_active = (v_cnt >= vbp && v_cnt < vfp);
//////////////////////////////////////////////////////////////////////////
// VGA Sync Generation
//
always @(posedge clk14 or posedge rst)
begin
if (rst)
begin
h_cnt <= 10'd0;
v_cnt <= 10'd0;
v_dot <= 5'd0;
end
else
begin
if (h_cnt < h_pixels)
h_cnt <= h_cnt + 1;
else
begin
// reset horizontal counters
h_cnt <= 0;
if (v_cnt < v_lines)
begin
v_cnt <= v_cnt + 1;
// count 20 rows, so 480px / 20 = 24 rows
if (v_active)
begin
v_dot <= v_dot + 1; // +1
if (v_dot == 5'd7) // == d19
v_dot <= 0;
end
end
else
begin
// reset vertical counters
v_cnt <= 0;
v_dot <= 0;
end
end
end
end
// count 16 pixels, so 640px / 16 = 40 characters
assign h_dot = h_active ? h_cnt[3:0] : 4'd0;
//////////////////////////////////////////////////////////////////////////
// Character ROM
font_rom font_rom(
.clk(clk14),
.mode(mode),
.character(font_char),
.pixel(font_pixel),
.line(font_line),
.out(font_out)
);
//////////////////////////////////////////////////////////////////////////
// Video RAM
vram vram(
.clk(clk14),
.read_addr(vram_r_addr),
.write_addr(vram_w_addr),
.r_en(h_active),
.w_en(vram_w_en),
.din(vram_din),
.dout(vram_dout)
);
//////////////////////////////////////////////////////////////////////////
// Video Signal Generation
always @(posedge clk14 or posedge rst) begin
if (rst) begin
vram_h_addr <= 'd0;
vram_v_addr <= 'd0;
end else begin
// start the pipeline for reading vram and font details
// 3 pixel clock cycles early
if (h_dot == 4'hC)
vram_h_addr <= vram_h_addr + 'd1;
// advance to next row when last display line is reached for row
if (v_dot == 5'd7 && h_cnt == 10'd0) // == d19
vram_v_addr <= vram_v_addr + 'd1;
// clear the address registers if we're not in visible area
if (~h_active)
vram_h_addr <= 'd0;
if (~v_active)
vram_v_addr <= vram_start_addr;
end
end
//////////////////////////////////////////////////////////////////////////
// Cursor blink
reg blink;
reg [22:0] blink_div;
always @(posedge clk14 or posedge rst)
begin
if (rst)
blink_div <= 0;
else
begin
blink_div <= blink_div + 1;
if (blink_div == 23'd0)
blink <= ~blink;
end
end
//////////////////////////////////////////////////////////////////////////
// Pipeline and VGA signals
// vram to font rom to display pipeline assignments
assign cursor = {v_cursor, h_cursor};
assign vram_r_addr = {vram_v_addr, vram_h_addr};
assign font_char = (vram_r_addr != cursor) ? vram_dout : (blink) ? 6'd0 : 6'd32;
assign font_pixel = h_dot + 1; // offset by one to get pixel into right cycle,
// font output one pixel clk behind
assign font_line = v_dot * 2 + 4;
// vga signals out to monitor
assign vga_red = (h_active & v_active) ? (font_out ? fg_colour[2] : bg_colour[2]) : 1'b0;
assign vga_grn = (h_active & v_active) ? (font_out ? fg_colour[1] : bg_colour[1]) : 1'b0;
assign vga_blu = (h_active & v_active) ? (font_out ? fg_colour[0] : bg_colour[0]) : 1'b0;
assign vga_h_sync = (h_cnt < h_pulse) ? 0 : 1;
assign vga_v_sync = (v_cnt < v_pulse) ? 0 : 1;
//////////////////////////////////////////////////////////////////////////
// CPU control and hardware cursor
assign vram_clr_addr = vram_end_addr + {3'd0, vram_v_addr[1:0]};
always @(posedge clk14 or posedge rst)
begin
if (rst)
begin
h_cursor <= 6'd0;
v_cursor <= 5'd0;
char_seen <= 'b0;
vram_start_addr <= 5'd0;
vram_end_addr <= 5'd24;
end
else
begin
vram_w_en <= 0;
if (clr_screen)
begin
// return to top of screen
h_cursor <= 6'd0;
v_cursor <= 5'd0;
vram_start_addr <= 5'd0;
vram_end_addr <= 5'd24;
// clear the screen
vram_w_addr <= {vram_v_addr, vram_h_addr};
vram_din <= 6'd32;
vram_w_en <= 1;
end
else
begin
// cursor overflow handling
if (h_cursor == 6'd40)
begin
h_cursor <= 6'd0;
v_cursor <= v_cursor + 'd1;
end
if (v_cursor == vram_end_addr)
begin
vram_start_addr <= vram_start_addr + 'd1;
vram_end_addr <= vram_end_addr + 'd1;
end
if (address == 1'b0) // address low == TX register
begin
if (enable & w_en & ~char_seen)
begin
// incoming character
char_seen <= 1;
case(din)
8'h0D,
8'h8D: begin
// handle carriage return
h_cursor <= 0;
v_cursor <= v_cursor + 'd1;
end
8'h00,
8'h0A,
8'h9B,
8'h7F: begin
// ignore the escape key
h_cursor <= 0;
end
default: begin
vram_w_addr <= cursor;
vram_din <= {~din[6], din[4:0]};
vram_w_en <= 1;
h_cursor <= h_cursor + 1;
end
endcase
end
else if(~enable & ~w_en)
char_seen <= 0;
end
else
begin
vram_w_addr <= {vram_clr_addr, vram_h_addr};
vram_din <= 6'd32;
vram_w_en <= 1;
end
end
end
end
endmodule

46
rtl/vram.v Normal file
View File

@ -0,0 +1,46 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: Video RAM for system
//
// Author.....: Alan Garfield
// Niels A. Moseley
// Date.......: 26-1-2018
//
module vram (
input clk, // clock signal
input [10:0] read_addr, // read address bus
input [10:0] write_addr, // write address bus
input r_en, // active high read enable strobe
input w_en, // active high write enable strobe
input [5:0] din, // 6-bit data bus (input)
output reg [5:0] dout // 6-bit data bus (output)
);
reg [5:0] ram_data[0:2047];
initial
$readmemb("roms/vga_vram.bin", ram_data, 0, 2047);
always @(posedge clk)
begin
if (r_en) dout <= ram_data[read_addr];
if (w_en) ram_data[write_addr] <= din;
end
endmodule