library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; package common is function log2(v: in natural) return natural; end package common; library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; package body common is function log2(v: in natural) return natural is variable n: natural; variable logn: natural; begin n := 1; for i in 0 to 128 loop logn := i; exit when (n>=v); n := n * 2; end loop; return logn; end function log2; end package body common; ---------------------------------------------------------------------------------- library IEEE, UNISIM; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; --use WORK.common.all; package ata is component pioIntfc generic( FREQ: natural := 50_000 -- operating frequency in KHz ); port( -- host side clk: in std_logic; -- master clock pioRst: in std_logic; -- async. reset pioRd: in std_logic; -- initiate read operation pioWr: in std_logic; -- initiate write operation pioAddr: in unsigned(4 downto 0); -- disk register address from host pioDIn: in unsigned(15 downto 0); -- data from host to disk pioDOut: out unsigned(15 downto 0); -- data from disk to host pioBusy: out std_logic; -- read or write operation is in progress pioIntrq: out std_logic; -- debounced interrupt from disk status: out std_logic_vector(3 downto 0); -- diagnostic status for the R/W -- disk side dior_n: out std_logic; -- disk register read-enable diow_n: out std_logic; -- disk register write-enable cs0_n: out std_logic; -- disk command block register select cs1_n: out std_logic; -- disk control block register select da: out unsigned(2 downto 0); -- disk register address ddIn: in unsigned(15 downto 0); -- data from disk ddOut: out unsigned(15 downto 0); -- data to disk ddOutEnbl: out std_logic; -- enable data outputs to disk intrq: in std_logic; -- interrupt from disk dmack_n: out std_logic -- DMA acknowledge ); end component; component ataCntl generic( FREQ: natural := 50_000 -- operating frequency in KHz ); port( -- host side rd: in std_logic; -- initiate read operation wr: in std_logic; -- initiate write operation abort: in std_logic; -- aborts read/write sector operation lba_mode: in std_logic; -- head: in unsigned(3 downto 0); -- disk head for data access -- cylinder: in unsigned(15 downto 0); -- cylinder for data access -- sector: in unsigned(7 downto 0); -- sector for data access lba: in unsigned(27 downto 0); clk: in std_logic; -- master clock rst: in std_logic; -- reset hDIn: in unsigned(15 downto 0); -- data from host to disk hDOut: out unsigned(15 downto 0); -- data from disk to host done: out std_logic; -- read or write operation is done status: out std_logic_vector(3 downto 0); -- diagnostic status -- disk side dior_n: out std_logic; -- disk register read-enable diow_n: out std_logic; -- disk register write-enable cs0_n: out std_logic; -- disk command block register select cs1_n: out std_logic; -- disk control block register select da: out unsigned(2 downto 0); -- register address --ddIn: in unsigned(15 downto 0); -- data from disk --ddOut: out unsigned(15 downto 0); -- data to disk --ddOutEnbl: out std_logic; -- enable data outputs to disk ddInOut: inout unsigned(15 downto 0); intrq: in std_logic; -- interrupt from disk dmack_n: out std_logic -- DMA acknowledge ); end component; end package ata; library IEEE, UNISIM; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; use WORK.common.all; ---------------------------------------------------------------------------------- -- This module executes a timed read/write operation to one of the disk registers. -- -- For a read operation, the host supplies a register address and pulls the read -- control line high. The read operation begins on the next rising clock edge -- and the busy signal goes high. The host gets the data from the disk register -- once the busy signal goes low again. -- -- For a write operation, the host supplies a register address and the data to -- be stored there and pulls the write control line high. The write operation -- begins on the nxt rising clock edge an the busy signal goes high. The register -- contains the new data once the busy signal goes low again. -- -- The 5-bit register address from the host contains the 3-bit disk register address -- along with the control and command block register select bits in the most -- significant bit positions. ---------------------------------------------------------------------------------- entity pioIntfc is generic( FREQ: natural := 50_000 -- operating frequency in KHz ); port( -- host side clk: in std_logic; -- master clock pioRst: in std_logic; -- async. reset pioRd: in std_logic; -- initiate read operation pioWr: in std_logic; -- initiate write operation pioAddr: in unsigned(4 downto 0); -- disk register address from host pioDIn: in unsigned(15 downto 0); -- data from host to disk pioDOut: out unsigned(15 downto 0); -- data from disk to host pioBusy: out std_logic; -- read or write operation is in progress pioIntrq: out std_logic; -- debounced interrupt from disk status: out std_logic_vector(3 downto 0); -- diagnostic status for the R/W -- disk side dior_n: out std_logic; -- disk register read-enable diow_n: out std_logic; -- disk register write-enable cs0_n: out std_logic; -- disk command block register select cs1_n: out std_logic; -- disk control block register select da: out unsigned(2 downto 0); -- disk register address ddIn: in unsigned(15 downto 0); -- data from disk ddOut: out unsigned(15 downto 0); -- data to disk ddOutEnbl: out std_logic; -- enable data outputs to disk intrq: in std_logic; -- interrupt from disk dmack_n: out std_logic -- DMA acknowledge ); end pioIntfc; architecture arch of pioIntfc is -- PIO mode 0 timing parameters in ns constant Top: natural := 600; -- minimum cycle time between R/W operations constant Tsetup: natural := 70; -- address/data setup before R/W pulse constant Tpulse: natural := 290; -- R/W pulse width constant Thold: natural := Top-Tsetup-Tpulse; -- address/data hold after R/W pulse -- PIO mode timing parameters converted into clock cycles (based on FREQ) constant NORM: natural := 1_000_000; -- normalize ns * KHz constant OP_CYCLES_N: natural := 1+((Top*FREQ)/NORM); constant SETUP_CYCLES_N: natural := 1+((Tsetup*FREQ)/NORM); constant PULSE_CYCLES_N: natural := 1+((Tpulse*FREQ)/NORM); constant HOLD_CYCLES_N: natural := 1+((Thold*FREQ)/NORM); -- timer register that counts down times for the phases of the disk R/W operation signal timer_r, timer_x: unsigned(log2(OP_CYCLES_N+1)-1 downto 0); -- PIO mode timing parameters converted into unsigned clock cycles for clarity constant OP_CYCLES: unsigned := TO_UNSIGNED(OP_CYCLES_N, timer_r'length); constant SETUP_CYCLES: unsigned := TO_UNSIGNED(SETUP_CYCLES_N, timer_r'length); constant PULSE_CYCLES: unsigned := TO_UNSIGNED(PULSE_CYCLES_N, timer_r'length); constant HOLD_CYCLES: unsigned := TO_UNSIGNED(HOLD_CYCLES_N, timer_r'length); -- states of the PIO interface state machine type cntlState is ( RW_SETUP, -- setup address/data before read pulse RD_PULSE, -- read pulse active RD_HOLD, -- hold address/data after read pulse WR_PULSE, -- write pulse active WR_HOLD -- hold address/data after write pulse ); signal state_r, state_x: cntlState; -- state register and next state -- PIO interface registers signal pioBusy_r, pioBusy_x: std_logic; -- R/W in-progress register signal dior_r, dior_x: std_logic; -- disk read signal register signal diow_r, diow_x: std_logic; -- disk write signal register signal da_r, da_x: unsigned(pioAddr'range); -- disk register address register signal ddOut_r, ddOut_x: unsigned(ddOut'range); -- data output to disk register signal ddOutEnbl_r, ddOutEnbl_x: std_logic; -- enable data output to disk register signal ddIn_r, ddIn_x: unsigned(ddIn'range); -- data input from disk register -- reports the status of the PIO interface signal status_r, status_x: std_logic_vector(3 downto 0); -- debounce counter for the interrupt request input signal intrqCnt_r,intrqCnt_x: unsigned(3 downto 0); constant DEBOUNCE_CNT: natural := 10; signal pioIntrq_r, pioIntrq_x: std_logic; signal intrq_r, intrq_x: std_logic; begin ----------------------------------------------------------- -- attach some internal signals to the host and disk ports ----------------------------------------------------------- dior_n <= dior_r; diow_n <= diow_r; da <= da_r(da'range); cs0_n <= da_r(3); cs1_n <= da_r(4); ddOut <= ddOut_r; ddOutEnbl <= ddOutEnbl_r; pioDOut <= ddIn_r; -- output data to host is the input data from the disk pioBusy <= pioBusy_r; pioIntrq <= pioIntrq_r; status <= status_r; dmack_n <= '1'; -- never acknowledge DMA requests from disk ----------------------------------------------------------- -- debounce the interrupt signal from the disk ----------------------------------------------------------- debounce: process(intrq,intrqCnt_r,intrq_r,pioIntrq_r) begin intrq_x <= intrq; pioIntrq_x <= pioIntrq_r; if(intrq = intrq_r) then if(intrqCnt_r = DEBOUNCE_CNT) then intrqCnt_x <= (others=>'0'); pioIntrq_x <= intrq_r; else intrqCnt_x <= intrqCnt_r + 1; end if; else intrqCnt_x <= (others=>'0'); end if; end process debounce; ----------------------------------------------------------- -- compute the next state and outputs ----------------------------------------------------------- combinatorial: process(pioRd,pioWr,pioAddr,pioDIn,state_r,timer_r,dior_r,pioBusy_r, diow_r,da_r,ddOut_r,ddOutEnbl_r,ddIn_r,ddIn,status_r) begin ----------------------------------------------------------- -- setup default values for signals ----------------------------------------------------------- state_x <= state_r; dior_x <= dior_r; diow_x <= diow_r; da_x <= da_r; ddOut_x <= ddOut_r; ddOutEnbl_x <= ddOutEnbl_r; ddIn_x <= ddIn_r; pioBusy_x <= pioBusy_r; status_x <= status_r; ----------------------------------------------------------- -- update the timers ----------------------------------------------------------- -- main timer for sequencing the phases of the R/W waveforms if timer_r /= 0 then -- decrement the timer and do nothing else since the previous -- phase has not completed yet. timer_x <= timer_r - 1; else -- the previous phase has completed once the timer hits zero. -- By default, leave the timer at zero. A R/W op will set it -- to non-zero below. timer_x <= timer_r; ----------------------------------------------------------- -- compute the next state and outputs ----------------------------------------------------------- case state_r is ----------------------------------------------------------- -- wait for a disk read or write operation ----------------------------------------------------------- when RW_SETUP => dior_x <= '1'; -- don't read or write the disk until requested diow_x <= '1'; ddOutEnbl_x <= '0'; -- don't drive disk data bus until requested if(pioRd = '1') then -- a read operation is requested pioBusy_x <= '1'; -- set busy bit da_x <= pioAddr; -- output disk register address timer_x <= SETUP_CYCLES; -- set timer for address setup state_x <= RD_PULSE; -- next state after address setup completes status_x <= "0010"; elsif(pioWr = '1') then -- a write operation is requested pioBusy_x <= '1'; -- set busy bit da_x <= pioAddr; -- output disk register address ddOut_x <= pioDIn; -- output data to disk ddOutEnbl_x <= '1'; -- enable output bus timer_x <= SETUP_CYCLES; -- set timer for address/data setup state_x <= WR_PULSE; -- next state after address/data setup completes status_x <= "0100"; else -- no operation is requested pioBusy_x <= '0'; -- clear busy bit state_x <= RW_SETUP; -- return to this state and wait for R/W request status_x <= "0001"; end if; ----------------------------------------------------------- -- pulse disk read control signal ----------------------------------------------------------- when RD_PULSE => dior_x <= '0'; -- lower disk read control line timer_x <= PULSE_CYCLES; -- load duration of read pulse state_x <= RD_HOLD; -- next state after pulse completes ----------------------------------------------------------- -- get data and hold address after read pulse ends ----------------------------------------------------------- when RD_HOLD => ddIn_x <= ddIn; -- load the data from the disk dior_x <= '1'; -- terminate the read pulse timer_x <= HOLD_CYCLES; -- insert hold period after read operation state_x <= RW_SETUP; -- look for another operation after the hold period ----------------------------------------------------------- -- pulse disk write control signal ----------------------------------------------------------- when WR_PULSE => diow_x <= '0'; -- lower disk write control line timer_x <= PULSE_CYCLES; -- load duration of write pulse state_x <= WR_HOLD; -- next state after pulse completes ----------------------------------------------------------- -- hold address and data after write pulse ends ----------------------------------------------------------- when WR_HOLD => diow_x <= '1'; -- terminate the write pulse timer_x <= HOLD_CYCLES; -- insert hold period after write operation state_x <= RW_SETUP; -- look for another operation after the hold period ----------------------------------------------------------- -- unknown state ----------------------------------------------------------- when others => state_x <= RW_SETUP; -- reset state if in erroneous state status_x <= "1000"; end case; end if; end process combinatorial; ----------------------------------------------------------- -- update registers on the appropriate clock edge ----------------------------------------------------------- update: process(pioRst,clk) begin if pioRst = '1' then -- asynchronous reset state_r <= RW_SETUP; timer_r <= (others=>'0'); pioBusy_r <= '0'; pioIntrq_r <= '0'; intrq_r <= '0'; intrqCnt_r <= (others=>'0'); dior_r <= '1'; diow_r <= '1'; da_r <= (others=>'0'); ddOut_r <= (others=>'0'); ddOutEnbl_r <= '0'; ddIn_r <= (others=>'0'); status_r <= (others=>'0'); elsif clk'event and clk='1' then state_r <= state_x; timer_r <= timer_x; pioBusy_r <= pioBusy_x; pioIntrq_r <= pioIntrq_x; intrq_r <= intrq_x; intrqCnt_r <= intrqCnt_x; dior_r <= dior_x; diow_r <= diow_x; da_r <= da_x; ddOut_r <= ddOut_x; ddOutEnbl_r <= ddOutEnbl_x; ddIn_r <= ddIn_x; status_r <= status_x; end if; end process update; end arch; library IEEE, UNISIM; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; use WORK.common.all; use WORK.ata.all; ---------------------------------------------------------------------------------- -- This module reads or write a complete 256-word sector of the disk. -- -- For a read operation, the host supplies a register address and pulls the read -- control line high. The read operation begins on the next rising clock edge -- and the busy signal goes high. The host gets the data from the disk register -- once the busy signal goes low again. -- -- For a write operation, the host supplies a register address and the data to -- be stored there and pulls the write control line high. The write operation -- begins on the nxt rising clock edge an the busy signal goes high. The register -- contains the new data once the busy signal goes low again. -- -- The 5-bit register address from the host contains the 3-bit disk register address -- along with the control and command block register select bits in the most -- significant bit positions. ---------------------------------------------------------------------------------- entity ataCntl is generic( FREQ: natural := 50_000 -- operating frequency in KHz ); port( -- host side rd: in std_logic; -- initiate read operation wr: in std_logic; -- initiate write operation abort: in std_logic; -- aborts read/write sector operation lba_mode: in std_logic; -- head: in unsigned(3 downto 0); -- disk head for data access -- cylinder: in unsigned(15 downto 0); -- cylinder for data access -- sector: in unsigned(7 downto 0); -- sector for data access lba: in unsigned(27 downto 0); clk: in std_logic; -- master clock rst: in std_logic; -- reset hDIn: in unsigned(15 downto 0); -- data from host to disk hDOut: out unsigned(15 downto 0); -- data from disk to host done: out std_logic; -- read or write operation is done status: out std_logic_vector(3 downto 0); -- diagnostic status -- disk side dior_n: out std_logic; -- disk register read-enable diow_n: out std_logic; -- disk register write-enable cs0_n: out std_logic; -- disk command block register select cs1_n: out std_logic; -- disk control block register select da: out unsigned(2 downto 0); -- register address -- ddIn: in unsigned(15 downto 0); -- data from disk -- ddOut: out unsigned(15 downto 0); -- data to disk -- ddOutEnbl: out std_logic; -- enable data outputs to disk ddInOut: inout unsigned(15 downto 0); intrq: in std_logic; -- interrupt from disk dmack_n: out std_logic -- DMA acknowledge ); end ataCntl; architecture arch of ataCntl is -- disk register addresses: (cs1,cs0,da2,da1,da0) constant CONTROL_REG: unsigned(4 downto 0) := "01110"; constant DATA_REG: unsigned(4 downto 0) := "10000"; constant SECTOR_CNT_REG: unsigned(4 downto 0) := "10010"; constant SECTOR_REG: unsigned(4 downto 0) := "10011"; constant CYL_LOW_REG: unsigned(4 downto 0) := "10100"; constant CYL_HIGH_REG: unsigned(4 downto 0) := "10101"; constant DRIVE_HEAD_REG: unsigned(4 downto 0) := "10110"; constant CMD_REG: unsigned(4 downto 0) := "10111"; constant STATUS_REG: unsigned(4 downto 0) := "10111"; -- commands for disk constant DRIVE_RESET_CMD: unsigned(7 downto 0) := x"0C"; constant DRIVE_RELEASE_CMD: unsigned(7 downto 0) := x"08"; constant NOP_CMD: unsigned(7 downto 0) := x"00"; constant READ_SECTOR_CMD: unsigned(7 downto 0) := x"20"; constant WRITE_SECTOR_CMD: unsigned(7 downto 0) := x"30"; -- disk status register bits constant STATUS_REG_BSY: natural := 7; constant STATUS_REG_DRQ: natural := 3; constant STATUS_REG_ERR: natural := 0; -- number of 16-bit words per disk sector --constant SECTOR_SIZE_N: natural := 256; -- states of the ATA drive interface state machine type cntlState is ( WAIT_FOR_NOT_BUSY, WAIT_FOR_NOT_BUSY_1, RESET_DRIVE, RESET_DRIVE_1, ABORT_1, WAIT_FOR_CMD, SETUP_DRIVE, SETUP_DRIVE_1, SETUP_DRIVE_2, SETUP_DRIVE_3, SETUP_DRIVE_4, SETUP_DRIVE_5, WRITE_WORDS, WRITE_WORDS_1, WRITE_WORDS_2, WRITE_WORDS_3, WRITE_WORDS_4, READ_WORDS, READ_WORDS_1, READ_WORDS_2, ERROR ); signal state_r, state_x: cntlState; -- state register and next state signal rtnState_r, rtnState_x: cntlState; -- state register and next state -- ATA drive interface registers signal cmd_r, cmd_x: unsigned(7 downto 0); -- ATA drive command (e.g. READ_SECTOR) signal wordCnt_r, wordCnt_x: unsigned(7 downto 0); -- counts words read from sector signal status_r, status_x: std_logic_vector(status'range); constant ERROR_FLAG: natural := 0; -- position of error flag bit in status register constant SECTOR_SIZE: unsigned(wordCnt_r'range) := TO_UNSIGNED(256,wordCnt_r'length); -- PIO interface signals signal pioRd: std_logic; -- initiate read operation signal pioWr: std_logic; -- initiate write operation signal pioBusy: std_logic; -- read or write operation in-progress signal pioIntrq: std_logic; -- debounced interrupt from disk signal pioAddr: unsigned(4 downto 0); -- register address from host signal pioDIn: unsigned(15 downto 0); -- data from host to disk signal pioDOut: unsigned(15 downto 0); -- data from disk to host -- ATA In/Out signal ddIn: unsigned(15 downto 0); -- data from disk signal ddOut: unsigned(15 downto 0); -- data to disk signal ddOutEnbl: std_logic; -- enable data outputs to disk signal head: unsigned(3 downto 0); -- disk head for data access signal cylinder: unsigned(15 downto 0); -- cylinder for data access signal sector: unsigned(7 downto 0); -- sector for data access begin ddInOut <= ddOut when ddOutEnbl = '1' else (others => 'Z'); ddIn <= ddInOut; head <= lba(27 downto 24); cylinder <= lba(23 downto 8); sector <= lba(7 downto 0); ------------------------------------------------------------------- -- instantiate the low-level interface to the disk registers ------------------------------------------------------------------- u0: pioIntfc generic map( FREQ => FREQ ) port map( clk => clk, pioRst => rst, pioRd => pioRd, pioWr => pioWr, pioAddr => pioAddr, pioDIn => pioDIn, pioDOut => pioDOut, pioBusy => pioBusy, pioIntrq => pioIntrq, status => open, dior_n => dior_n, diow_n => diow_n, cs0_n => cs0_n, cs1_n => cs1_n, da => da, ddIn => ddIn, ddOut => ddOut, ddOutEnbl => ddOutEnbl, intrq => intrq, dmack_n => dmack_n ); ----------------------------------------------------------- -- attach some internal signals to the host and disk ports ----------------------------------------------------------- hdOut <= pioDOut; status <= status_r; ----------------------------------------------------------- -- compute the next state and outputs ----------------------------------------------------------- combinatorial: process(state_r,rd,wr,head,cylinder,sector,hDIn,cmd_r,pioDOut, status_r,pioBusy,wordCnt_r,rtnState_r, lba_mode, pioIntrq) begin ----------------------------------------------------------- -- setup default values for signals ----------------------------------------------------------- done <= '0'; pioRd <= '0'; pioWr <= '0'; pioDIn <= (others=>'0'); pioAddr <= (others=>'0'); state_x <= state_r; rtnState_x <= rtnState_r; cmd_x <= cmd_r; wordCnt_x <= wordCnt_r; status_x <= status_r; if(pioBusy = '0') then ----------------------------------------------------------- -- compute the next state and outputs ----------------------------------------------------------- case state_r is ----------------------------------------------------------------- -- set and then clear the reset bit in the disk control register ----------------------------------------------------------------- when RESET_DRIVE => pioWr <= '1'; pioAddr <= CONTROL_REG; pioDIn(7 downto 0) <= DRIVE_RESET_CMD; state_x <= RESET_DRIVE_1; when RESET_DRIVE_1 => pioWr <= '1'; pioAddr <= CONTROL_REG; pioDIn(7 downto 0) <= DRIVE_RELEASE_CMD; state_x <= WAIT_FOR_NOT_BUSY; -- wait for the drive to go non-busy after reset ----------------------------------------------------------------- -- abort the current command by issuing a NOP command ----------------------------------------------------------------- when ABORT_1 => pioWr <= '1'; pioAddr <= CMD_REG; pioDIn(7 downto 0) <= NOP_CMD; state_x <= WAIT_FOR_NOT_BUSY; rtnState_x <= WAIT_FOR_CMD; ----------------------------------------------------------------- -- wait for the busy bit in the disk status register to clear ----------------------------------------------------------------- when WAIT_FOR_NOT_BUSY => pioRd <= '1'; pioAddr <= STATUS_REG; state_x <= WAIT_FOR_NOT_BUSY_1; when WAIT_FOR_NOT_BUSY_1 => if(pioDOut(STATUS_REG_BSY) = '1') then state_x <= WAIT_FOR_NOT_BUSY; -- busy bit not clear so check it again else state_x <= rtnState_r; -- pop the state registers end if; ----------------------------------------------------------------- -- wait for a read or write sector command ----------------------------------------------------------------- when WAIT_FOR_CMD => if(rd = '1') then -- read a sector of the disk wordCnt_x <= SECTOR_SIZE-1; -- set number of words to read from sector cmd_x <= READ_SECTOR_CMD; -- command for rading a sector state_x <= SETUP_DRIVE; -- setup the disk registers for reading the sector rtnState_x <= READ_WORDS; elsif(wr = '1') then -- write a sector of the disk wordCnt_x <= SECTOR_SIZE-1; -- set number of words to write to sector cmd_x <= WRITE_SECTOR_CMD; -- command for writing a sector state_x <= SETUP_DRIVE; -- setup the disk registers for writing the sector rtnState_x <= WRITE_WORDS; else -- no operation requested state_x <= WAIT_FOR_CMD; -- keep waiting for a R/W command end if; ----------------------------------------------------------------- -- setup the disk to read or write a sector ----------------------------------------------------------------- when SETUP_DRIVE => -- load the sector count register with 1 since only a single sector will be read/written pioWr <= '1'; pioAddr <= SECTOR_CNT_REG; pioDIn <= TO_UNSIGNED(1,pioDIn'length); state_x <= SETUP_DRIVE_1; when SETUP_DRIVE_1 => -- load the number of the sector that will be read/written pioWr <= '1'; pioAddr <= SECTOR_REG; pioDIn(7 downto 0) <= sector; state_x <= SETUP_DRIVE_2; when SETUP_DRIVE_2 => -- load the number of the cylinder that contains the desired sector pioWr <= '1'; pioAddr <= CYL_LOW_REG; pioDIn(7 downto 0) <= cylinder(7 downto 0); state_x <= SETUP_DRIVE_3; when SETUP_DRIVE_3 => pioWr <= '1'; pioAddr <= CYL_HIGH_REG; pioDIn(7 downto 0) <= cylinder(15 downto 8); state_x <= SETUP_DRIVE_4; when SETUP_DRIVE_4 => -- load the number of the head that accesses the desired cylinder pioWr <= '1'; pioAddr <= DRIVE_HEAD_REG; pioDIn(7 downto 0) <= '1' & lba_mode & "10" & head; state_x <= SETUP_DRIVE_5; when SETUP_DRIVE_5 => -- load the command (read sector or write sector) pioWr <= '1'; pioAddr <= CMD_REG; pioDIn(cmd_r'range) <= cmd_r; state_x <= rtnState_r; ----------------------------------------------------------------- -- write data to the disk sector ----------------------------------------------------------------- when WRITE_WORDS => state_x <= WAIT_FOR_NOT_BUSY; rtnState_x <= WRITE_WORDS_1; when WRITE_WORDS_1 => -- check the data request bit to see if the sector is ready for read/write ops if(pioDOut(STATUS_REG_DRQ) = '1') then state_x <= WRITE_WORDS_2; -- if ready, then write sector state elsif(pioDOut(STATUS_REG_ERR) = '1') then status_x(ERROR_FLAG) <= '1'; -- tell the host there was an error state_x <= ERROR; else -- data not ready, so read status again pioRd <= '1'; pioAddr <= STATUS_REG; state_x <= WRITE_WORDS_1; end if; when WRITE_WORDS_2 => -- write another word to the disk each time the write control is high if(wr = '1') then pioWr <= '1'; pioAddr <= DATA_REG; pioDIn <= hDIn; state_x <= WRITE_WORDS_3; else state_x <= WRITE_WORDS_2; -- halt writes to disk when wr is inactive end if; when WRITE_WORDS_3 => done <= '1'; -- tell the host this word has been written to disk if(wordCnt_r = 0) then -- return and wait for another command if the entire sector has been written state_x <= WRITE_WORDS_4; else -- otherwise, write the next word to the disk wordCnt_x <= wordCnt_r - 1; state_x <= WRITE_WORDS_2; end if; when WRITE_WORDS_4 => if(pioIntrq = '1') then pioRd <= '1'; pioAddr <= STATUS_REG; state_x <= WAIT_FOR_CMD; else state_x <= WRITE_WORDS_4; end if; ----------------------------------------------------------------- -- read data from the disk sector ----------------------------------------------------------------- when READ_WORDS => if(pioIntrq = '1') then pioRd <= '1'; pioAddr <= STATUS_REG; state_x <= READ_WORDS_1; else state_x <= READ_WORDS; end if; when READ_WORDS_1 => -- read another word from the disk each time the read control is high if(rd = '1') then pioRd <= '1'; pioAddr <= DATA_REG; state_x <= READ_WORDS_2; else state_x <= READ_WORDS_1; -- halt reads from disk when rd is inactive end if; when READ_WORDS_2 => done <= '1'; -- tell the host a word has been read and is available if(wordCnt_r = 0) then -- return and wait for another command if the entire sector has been read state_x <= WAIT_FOR_CMD; else -- otherwise, read the next word from the disk wordCnt_x <= wordCnt_r - 1; state_x <= READ_WORDS_1; end if; ----------------------------------------------------------------- -- error state. Requires a reset to leave this state ----------------------------------------------------------------- when ERROR => status_x(ERROR_FLAG) <= '1'; state_x <= ERROR; ----------------------------------------------------------------- -- unknown state. Go to the error state ----------------------------------------------------------------- when others => status_x(ERROR_FLAG) <= '1'; state_x <= ERROR; end case; end if; end process combinatorial; ----------------------------------------------------------- -- update registers on the appropriate clock edge ----------------------------------------------------------- update: process(rst,clk) begin if rst = '1' then -- asynchronous reset state_r <= RESET_DRIVE; -- upon reset, reset the drive rtnState_r <= WAIT_FOR_CMD; -- and then wait for read/write commands cmd_r <= (others=>'0'); wordCnt_r <= (others=>'0'); status_r <= (others=>'0'); -- clear the status register elsif clk'event and clk='1' then if(abort = '1') then -- abort a read or write sector command and go back to wait for another command state_r <= ABORT_1; else state_r <= state_x; end if; rtnState_r <= rtnState_x; cmd_r <= cmd_x; wordCnt_r <= wordCnt_x; status_r <= status_x; end if; end process update; end arch;