Files
MultiComp_MiSTer/Components/SDCARD/image_controller.vhd
Fred VanEijk b4c9d45ed1 add image controller - for using images as cp/m disk
increase read buffer of UART from 16 to 64 bytes
allow selection of UART baud rate
allow selection of sd controller or image controller
update readme.md with more explanation of above and more detail on MultiComp use specifically for CP/M
2024-11-01 09:04:45 -04:00

141 lines
4.8 KiB
VHDL

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity image_controller is
port (
-- System interface
clk : in std_logic;
n_reset : in std_logic;
-- CPU interface
n_rd : in std_logic;
n_wr : in std_logic;
dataIn : in std_logic_vector(7 downto 0);
dataOut : out std_logic_vector(7 downto 0);
regAddr : in std_logic_vector(2 downto 0);
-- Status output
driveLED : out std_logic := '1'
);
end image_controller;
architecture rtl of image_controller is
-- Status register bits
constant STATUS_WRITE_READY : integer := 7;
constant STATUS_READ_READY : integer := 6;
constant STATUS_BUSY : integer := 5;
-- Internal registers
signal status : std_logic_vector(7 downto 0);
signal block_busy : std_logic := '0';
signal address : std_logic_vector(31 downto 0) := (others => '0');
-- Data buffering
signal read_data : std_logic_vector(7 downto 0);
signal write_data : std_logic_vector(7 downto 0);
signal buffer_addr : unsigned(8 downto 0); -- 0-511 bytes per sector
-- Transfer state machine
type transfer_state is (IDLE, READ_SECTOR, WRITE_SECTOR, WAIT_COMPLETE);
signal state : transfer_state := IDLE;
-- Activity LED timer
signal led_counter : integer range 0 to 1000000 := 0;
begin
-- Address register handling
process(n_wr)
begin
if rising_edge(n_wr) then
case regAddr is
when "010" => address(7 downto 0) <= dataIn;
when "011" => address(15 downto 8) <= dataIn;
when "100" => address(23 downto 16) <= dataIn;
when others => null;
end case;
end if;
end process;
-- Command and status handling
process(clk, n_reset)
begin
if n_reset = '0' then
state <= IDLE;
block_busy <= '0';
buffer_addr <= (others => '0');
led_counter <= 0;
status <= x"80"; -- Ready to write
elsif rising_edge(clk) then
-- Default LED timeout behavior
if led_counter /= 0 then
led_counter <= led_counter - 1;
end if;
-- Update status register
if state = IDLE then
status(STATUS_WRITE_READY) <= '1';
status(STATUS_READ_READY) <= '0';
elsif state = READ_SECTOR then
status(STATUS_WRITE_READY) <= '0';
status(STATUS_READ_READY) <= '1';
else
status(STATUS_WRITE_READY) <= '0';
status(STATUS_READ_READY) <= '0';
end if;
status(STATUS_BUSY) <= block_busy;
case state is
when IDLE =>
if n_wr = '0' and regAddr = "001" then
case dataIn is
when x"00" => -- Read command
state <= READ_SECTOR;
block_busy <= '1';
buffer_addr <= (others => '0');
led_counter <= 1000000; -- 1M cycles = 20ms at 50MHz
when x"01" => -- Write command
state <= WRITE_SECTOR;
block_busy <= '1';
buffer_addr <= (others => '0');
led_counter <= 1000000;
when others =>
null;
end case;
end if;
when READ_SECTOR =>
if buffer_addr = 511 then
state <= IDLE;
block_busy <= '0';
elsif n_rd = '0' and regAddr = "000" then
buffer_addr <= buffer_addr + 1;
end if;
when WRITE_SECTOR =>
if buffer_addr = 511 then
state <= IDLE;
block_busy <= '0';
elsif n_wr = '0' and regAddr = "000" then
write_data <= dataIn;
buffer_addr <= buffer_addr + 1;
end if;
when others =>
state <= IDLE;
end case;
end if;
end process;
-- Data output multiplexing
dataOut <= status when regAddr = "001" else
read_data when regAddr = "000" else
x"FF";
-- Drive LED control
driveLED <= '0' when led_counter /= 0 else '1';
end rtl;