-- Interactive Audio Manipulation Processor -- -- file: ram_bench.vhd -- status: simulated, no known errors -- -- author: Stephen Tang -- -- test bench for the RAM interface use std.textio.all; library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; library work; use work.ram_interface; -- the test bench has no inputs or outputs entity ram_bench is end ram_bench; architecture behavioural of ram_bench is constant SAMPLE_WIDTH: positive := 24; -- ADC provides a 24-bit sample, but... constant EFFECTIVE_SAMPLE_WIDTH: positive := 8; -- ...we only use the 8 most significant bits. constant DELAY_WIDTH: positive := 5; constant NO_DELAY: std_logic_vector(DELAY_WIDTH-1 downto 0) := "00000"; -- RAM constants constant RAM_ADDRESS_WIDTH: positive := 18; constant RAM_DATA_WIDTH: positive := 8; constant RAM_MAX_ADDRESS: std_logic_vector(RAM_ADDRESS_WIDTH-1 downto 0) := "000000000000000100"; -- up to 4 samples allowed constant HalfPeriod : time := 40 ns; component ram_interface port( clock: in std_logic; -- should be 25.175MHz reset: in std_logic; -- active low, from reset pushbutton -- RAM interface ram_ack_in: in std_logic; -- RAM driver has serviced the request ram_request: out std_logic; -- request services of RAM driver ram_rw: out std_logic; -- indicate request type: R (1) or W (0) ram_address: buffer std_logic_vector(RAM_ADDRESS_WIDTH-1 downto 0); ram_data: inout std_logic_vector(RAM_DATA_WIDTH-1 downto 0); -- ADC interface adc_ready: in std_logic; -- ADC has a sample ready adc_ack: out std_logic; -- tell ADC that the sample was received adc_sample_in: in std_logic_vector(EFFECTIVE_SAMPLE_WIDTH-1 downto 0); -- incoming sample from ADC -- audio path interface a_delay: in std_logic_vector(DELAY_WIDTH-1 downto 0); -- delay of the sample to retrieve a_request: in std_logic; -- audio path wants a sample a_ack: out std_logic; -- sample has been sent a_sample: out std_logic_vector(EFFECTIVE_SAMPLE_WIDTH-1 downto 0) -- the sample requested by the audio path -- the following signal is only used in ram_bench2 -- ;CurrentAddress: out std_logic_vector(RAM_ADDRESS_WIDTH-1 downto 0) ); end component; component bidir port( bidir : INOUT STD_LOGIC_VECTOR (RAM_DATA_WIDTH-1 DOWNTO 0); oe, clk : IN STD_LOGIC; inp : IN STD_LOGIC_VECTOR (RAM_DATA_WIDTH-1 DOWNTO 0); outp : OUT STD_LOGIC_VECTOR (RAM_DATA_WIDTH-1 DOWNTO 0)); end component; -- interfacing signals for the DUT signal clock: std_logic := '0'; signal reset: std_logic := '0'; signal RamDone, RamRequest, RamRW: std_logic; signal RamAddress: std_logic_vector(RAM_ADDRESS_WIDTH-1 downto 0); signal RamData: std_logic_vector(RAM_DATA_WIDTH-1 downto 0); signal SampleReady, SampleAccepted: std_logic; signal SampleFromADC: std_logic_vector(SAMPLE_WIDTH-1 downto 0); signal Delay: std_logic_vector(DELAY_WIDTH-1 downto 0); signal SampleRequest, SampleSentToAudio: std_logic; signal SampleFromRam: std_logic_vector(EFFECTIVE_SAMPLE_WIDTH-1 downto 0); -- the following signal is not in ram_bench -- signal CurrentAddress: std_logic_vector(RAM_ADDRESS_WIDTH-1 downto 0); -- file I/O stuff type std_logic_vector_file is file of std_logic_vector(SAMPLE_WIDTH-1 downto 0); --signal SampleFromFile: std_logic_vector(SAMPLE_WIDTH-1 downto 0); -- RAM stub stuff signal DataIntoRam,DataOutOfRam: std_logic_vector(RAM_DATA_WIDTH-1 downto 0); type RamStates is (RS_WAITING, RS_TRANSFER); signal RamState: RamStates; subtype word is std_logic_vector(7 downto 0); type ram_array is array (0 to 3) of word; signal TheRam: ram_array; signal CountOut: std_logic_vector(7 downto 0); begin DUT: ram_interface port map( clock => clock, reset => reset, -- RAM interface ram_ack_in => RamDone, ram_request => RamRequest, ram_rw => RamRW, ram_address => RamAddress, ram_data => RamData, -- ADC interface adc_ready => SampleReady, adc_ack => SampleAccepted, adc_sample_in => SampleFromADC(SAMPLE_WIDTH-1 downto SAMPLE_WIDTH-EFFECTIVE_SAMPLE_WIDTH), -- audio path interface a_delay => Delay, a_request => SampleRequest, a_ack => SampleSentToAudio, a_sample => SampleFromRam -- the following line is exclusive to ram_bench2 -- ,CurrentAddress => CurrentAddress ); RAMdataBus: bidir port map( bidir => RamData, oe => RamRW, -- OE='0' puts line to 'Z', OE='1' lets me drive the bus -- RW='0' means write, RW='1' means read clk => clock, inp => DataOutOfRam, -- data to be dumped on the bus outp => DataIntoRam -- data to be received from bus when OE='0' ); -- after an initial delay, force reset high for remainder of simulation -- (remember that reset is active low) reset <= '1' after 2*HalfPeriod; -- create a clock pulse clock <= not clock after HalfPeriod; ADCstub: process(clock) -- binary file I/O stuff file ADCsampleFile: std_logic_vector_file is "adcsamples"; variable SampleFromFile: std_logic_vector(SAMPLE_WIDTH-1 downto 0);--line; variable CountTicks: std_logic_vector(7 downto 0); begin if rising_edge(clock) then if reset = '0' then CountTicks := "00000000"; SampleReady <= '0'; end if; if SampleAccepted = '1' then SampleReady <= '0'; end if; -- count to 16; when count reaches 16, -- it's time to update the value -- of the ADC sample -- but only update the value when the ram interface is ready CountTicks := CountTicks + '1'; if CountTicks >= "00010000" then -- if SampleReady='0', that means the ram interface had -- accepted the latest sample. Hence, after waiting for one more -- clock cycle, we can put out a new -- sample. If SampleReady='1', then the ram interface has not -- accepted the latest sample, so just hold the CountTick value -- until the ram interface is ready. if SampleReady = '0' then if CountTicks = "00010001" then CountTicks := "00000000"; if not endfile(ADCsampleFile) then read(ADCsampleFile, SampleFromFile); SampleFromADC <= SampleFromFile; SampleReady <= '1'; end if; end if; else CountTicks := CountTicks - '1'; end if; end if; CountOut <= CountTicks; end if; end process ADCstub; RamStub: process(clock) variable index: integer; variable localRW: std_logic; variable localAddress: std_logic_vector(RAM_ADDRESS_WIDTH-1 downto 0); variable tempAddress: std_logic_vector(RAM_ADDRESS_WIDTH-1 downto 0); variable DelayInRorW: std_logic_vector(1 downto 0); begin if rising_edge(clock) then case RamState is when RS_WAITING => DataOutOfRam <= (others => '0'); -- remember not to drive ram data bus directly RamDone <= '0'; if RamRequest = '1' then DelayInRorW := "00"; localRW := RamRW; -- localAddress := RamAddress; RamState <= RS_TRANSFER; else RamState <= RS_WAITING; end if; when RS_TRANSFER => -- first order of business before we handle the read or -- write is to convert the std_logic_vector address -- into an integer tempAddress := (others => '0'); index := 0; while tempAddress /= RamAddress loop --localAddress loop tempAddress := tempAddress + '1'; index := index + 1; end loop; DelayInRorW := DelayInRorW + '1'; if DelayInRorW = "11" then if localRW = '1' then -- read DataOutOfRam <= TheRam(index); else -- write TheRam(index) <= DataIntoRam; end if; RamDone <= '1'; RamState <= RS_WAITING; elsif RamRequest = '0' then -- abort the ram operation; this transition from '1' to '0' -- probably happened on the same clock edge as the one -- that caused the transition to this state (RS_TRANSFER). RamState <= RS_WAITING; end if; when others => RamState <= RS_WAITING; end case; end if; end process RamStub; -- we can't use this! it causes RAM states to change over 2 clock cycles, -- which is too slow. The RamDone signal would remain high for 2 clocks, -- which would make the ram interface behave incorrectly. -- Also, the ram interface an the ram driver must run on the same clock. --RamStubStateRegister :process(clock) --begin -- if rising_edge(clock) then -- RamState <= RamNextState; -- end if; --end process RamStubStateRegister; -- temp code until I get the audio path stub fully functional Delay <= (others => '0'); AudioPathStub: process(clock) variable CountTicks: std_logic_vector(7 downto 0); -- the next variable is exclusive to ram_bench2 -- variable newDelay: std_logic_vector(DELAY_WIDTH-1 downto 0); begin if rising_edge(clock) then if reset = '0' then CountTicks := "00000000"; SampleRequest <= '0'; else if SampleSentToAudio = '1' then SampleRequest <= '0'; end if; -- count to 20; when count reaches 20, send out a new sample request -- if the previous sample request has been serviced. If the previous -- request has yet to be filled, delay this one for some time after -- the previous request is filled. This delay will ensure that the -- ram interface does not mistake the two requests for the same one. CountTicks := CountTicks + '1'; if CountTicks >= "00010100" then if SampleRequest = '0' then CountTicks := "00000000"; SampleRequest <= '1'; -- -- In a real system, with 512k of memory, it is unreasonable that -- -- a delay that is larger than the number of addresses would -- -- occur.Hence, if the delay reaches the maximum, reset its value. -- -- The next 6 lines are exclusive to ram_bench2 -- newDelay := Delay + '1'; -- if newDelay = RAM_MAX_ADDRESS then -- Delay <= (others => '0'); -- else -- Delay <= newDelay; -- end if; else -- subtracting has the effect of delaying when the next sample -- request will be made CountTicks := CountTicks - "100"; end if; end if; end if; CountOut <= CountTicks; end if; end process AudioPathStub; end behavioural;