URL
https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk
Subversion Repositories open8_urisc
Compare Revisions
- This comparison shows the changes necessary to convert path
/open8_urisc/trunk
- from Rev 322 to Rev 323
- ↔ Reverse comparison
Rev 322 → Rev 323
/VHDL/o8_mavg_8ch_16b_64d.vhd
0,0 → 1,327
-- Copyright (c)2023 Jeremy Seth Henry |
-- All rights reserved. |
-- |
-- Redistribution and use in source and binary forms, with or without |
-- modification, are permitted provided that the following conditions are met: |
-- * Redistributions of source code must retain the above copyright |
-- notice, this list of conditions and the following disclaimer. |
-- * Redistributions in binary form must reproduce the above copyright |
-- notice, this list of conditions and the following disclaimer in the |
-- documentation and/or other materials provided with the distribution, |
-- where applicable (as part of a user interface, debugging port, etc.) |
-- |
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``AS IS'' AND ANY |
-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
-- DISCLAIMED. IN NO EVENT SHALL JEREMY SETH HENRY BE LIABLE FOR ANY |
-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
-- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
-- |
-- VHDL units : o8_mavg_8ch_16b_64d |
-- Description: 8-channel moving average calculation for 16-bit unsigned data |
-- Accumulator depth is 64 elements, using 1 block RAM. |
-- |
-- Register Map: |
-- Offset Bitfield Description Read/Write |
-- 0x00 AAAAAAAA Raw Data (lower) (RW) |
-- 0x01 AAAAAAAA Raw Data (upper) (RW) |
-- 0x02 -----AAA Raw Channel Select (RW) |
-- 0x03 A------- Update Accum / Busy (RW) |
-- 0x04 AAAAAAAA Avg Data (lower) (RW) |
-- 0x05 AAAAAAAA Avg Data (upper) (RW) |
-- 0x06 -----AAA Avg Channel Select (RW) |
-- 0x07 A------- Flush Statistics / Busy (RW) |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.std_logic_arith.all; |
use ieee.std_logic_unsigned.all; |
use ieee.std_logic_misc.all; |
|
library work; |
use work.open8_pkg.all; |
|
entity o8_mavg_8ch_16b_64d is |
generic( |
Address : ADDRESS_TYPE |
); |
port( |
Open8_Bus : in OPEN8_BUS_TYPE; |
Write_Qual : in std_logic := '1'; |
Rd_Data : out DATA_TYPE; |
Interrupt : out std_logic |
); |
end entity; |
|
architecture behave of o8_mavg_8ch_16b_64d is |
|
alias Clock is Open8_Bus.Clock; |
alias Reset is Open8_Bus.Reset; |
|
constant User_Addr : std_logic_vector(15 downto 3) |
:= Address(15 downto 3); |
alias Comp_Addr is Open8_Bus.Address(15 downto 3); |
signal Addr_Match : std_logic; |
|
alias Reg_Sel_d is Open8_Bus.Address(2 downto 0); |
signal Reg_Sel_q : std_logic_vector(2 downto 0); |
signal Wr_En_d : std_logic := '0'; |
signal Wr_En_q : std_logic := '0'; |
alias Wr_Data_d is Open8_Bus.Wr_Data; |
signal Wr_Data_q : DATA_TYPE := x"00"; |
signal Rd_En_d : std_logic := '0'; |
signal Rd_En_q : std_logic := '0'; |
|
signal RAW_Data : std_logic_vector(15 downto 0) := (others => '0'); |
alias RAW_Data_L is RAW_Data(7 downto 0); |
alias RAW_Data_H is RAW_Data(15 downto 8); |
|
signal RAW_Channel : std_logic_vector(2 downto 0) := (others => '0'); |
|
signal RAW_Valid : std_logic := '0'; |
|
signal Flush_Valid : std_logic := '0'; |
signal Flush_Busy : std_logic := '0'; |
|
type AVG_CTL_STATES is (INIT, CLR_BUFF, IDLE, RD_LAST, ADV_PTR, CALC_NEXT, |
WR_NEW); |
signal AVG_Ctl : AVG_CTL_STATES := INIT; |
|
signal Avg_Busy : std_logic := '0'; |
|
signal CH_Select : std_logic_vector(2 downto 0) := (others => '0'); |
signal Data_New : std_logic_vector(15 downto 0) := (others => '0'); |
|
signal RAM_Wr_Addr : std_logic_vector(8 downto 0) := (others => '0'); |
alias RAM_Wr_Chan is RAM_Wr_Addr(8 downto 6); |
alias RAM_Wr_Ptr is RAM_Wr_Addr(5 downto 0); |
|
signal RAM_Wr_Data : std_logic_vector(15 downto 0) := (others => '0'); |
|
signal RAM_Wr_En : std_logic := '0'; |
|
signal RAM_Rd_Addr : std_logic_vector(8 downto 0) := (others => '0'); |
alias RAM_Rd_Chan is RAM_Rd_Addr(8 downto 6); |
alias RAM_Rd_Ptr is RAM_Rd_Addr(5 downto 0); |
|
signal RAM_Rd_Data : std_logic_vector(15 downto 0) := (others => '0'); |
alias Data_Old is RAM_Rd_Data; |
|
type PTR_ARRAY is array (0 to 7) of std_logic_vector(5 downto 0); |
signal SP0_Pointers : PTR_ARRAY; |
signal SPN_Pointers : PTR_ARRAY; |
|
-- Accumulator width is bus_size (16) + log depth (6) |
type ACCUM_ARRAY is array (0 to 7) of unsigned(21 downto 0); |
signal Accumulators : ACCUM_ARRAY; |
|
signal AVG_Channel : std_logic_vector(2 downto 0) := (others => '0'); |
|
signal AVG_Out : std_logic_vector(15 downto 0); |
alias AVG_Out_L is AVG_Out(7 downto 0); |
alias AVG_Out_H is AVG_Out(7 downto 0); |
|
begin |
|
Addr_Match <= '1' when Comp_Addr = User_Addr else '0'; |
Wr_En_d <= Addr_Match and Write_Qual and Open8_Bus.Wr_En; |
Rd_En_d <= Addr_Match and Open8_Bus.Rd_En; |
|
Register_IF_proc: process( Clock, Reset ) |
variable i : integer := 0; |
begin |
if( Reset = Reset_Level )then |
Wr_En_q <= '0'; |
Wr_Data_q <= x"00"; |
Reg_Sel_q <= (others => '0'); |
Rd_En_q <= '0'; |
Rd_Data <= OPEN8_NULLBUS; |
|
RAW_Data <= (others => '0'); |
RAW_Valid <= '0'; |
RAW_Channel <= (others => '0'); |
|
AVG_Out <= (others => '0'); |
AVG_Channel <= (others => '0'); |
|
|
elsif( rising_edge(Clock) )then |
Reg_Sel_q <= Reg_Sel_d; |
Wr_En_q <= Wr_En_d; |
Wr_Data_q <= Wr_Data_d; |
|
RAW_Valid <= '0'; |
|
if( Wr_En_q = '1' )then |
case( Reg_Sel_q )is |
when "000" => |
RAW_Data_L <= Wr_Data_q; |
|
when "001" => |
RAW_Data_H <= Wr_Data_q; |
|
when "010" => |
RAW_Channel <= Wr_Data_q(2 downto 0); |
|
when "011" => |
RAW_Valid <= not Avg_Busy; |
|
when "110" => |
AVG_Channel <= Wr_Data_q(2 downto 0); |
|
when "111" => |
Flush_Valid <= not Flush_Busy; |
|
when others => |
null; |
|
end case; |
end if; |
|
i := conv_integer(AVG_Channel); |
AVG_Out <= std_logic_vector(Accumulators(i)(21 downto 6)); |
|
Rd_Data <= OPEN8_NULLBUS; |
Rd_En_q <= Rd_En_d; |
if( Rd_En_q = '1' )then |
case( Reg_Sel_q )is |
when "000" => |
Rd_Data <= RAW_Data_L; |
|
when "001" => |
Rd_Data <= RAW_Data_H; |
|
when "010" => |
Rd_Data <= "00000" & RAW_Channel; |
|
when "011" => |
Rd_Data <= Avg_Busy & "0000000"; |
|
when "100" => |
Rd_Data <= AVG_Out_L; |
|
when "101" => |
Rd_Data <= AVG_Out_H; |
|
when "110" => |
Rd_Data <= "00000" & AVG_Channel; |
|
when "111" => |
Rd_Data <= Flush_Busy & "0000000"; |
|
when others => |
null; |
|
end case; |
end if; |
|
end if; |
end process; |
|
MAVG_Control_proc: process( Clock, Reset ) |
variable i : integer := 0; |
begin |
if( Reset = Reset_Level )then |
AVG_Ctl <= INIT; |
|
CH_Select <= (others => '0'); |
Data_New <= (others => '0'); |
|
Flush_Busy <= '0'; |
Avg_Busy <= '0'; |
|
for i in 0 to 7 loop |
SP0_Pointers(i) <= (others => '1'); |
SPN_Pointers(i) <= (others => '0'); |
Accumulators(i) <= (others => '0'); |
end loop; |
|
RAM_Wr_Addr <= (others => '0'); |
RAM_Wr_Data <= (others => '0'); |
RAM_Wr_En <= '0'; |
RAM_Rd_Addr <= (others => '0'); |
|
Interrupt <= '0'; |
|
elsif( rising_edge(Clock) )then |
|
Interrupt <= '0'; |
|
RAM_Wr_En <= '0'; |
|
Flush_Busy <= '0'; |
Avg_Busy <= '1'; |
|
i := conv_integer(unsigned(CH_Select)); |
|
case( AVG_Ctl )is |
when INIT => |
Flush_Busy <= '1'; |
RAM_Wr_Addr <= (others => '0'); |
RAM_Wr_Data <= (others => '0'); |
AVG_Ctl <= CLR_BUFF; |
|
when CLR_BUFF => |
Flush_Busy <= '1'; |
RAM_Wr_Addr <= RAM_Wr_Addr + 1; |
RAM_Wr_En <= '1'; |
if( and_reduce(RAM_Wr_Addr) = '1' )then |
AVG_Ctl <= IDLE; |
end if; |
|
when IDLE => |
Avg_Busy <= '0'; |
if( Flush_Valid = '1' )then |
AVG_Ctl <= INIT; |
elsif( RAW_Valid = '1' )then |
Data_New <= RAW_Data; |
CH_Select <= RAW_Channel; |
AVG_Ctl <= RD_LAST; |
end if; |
|
when RD_LAST => |
RAM_Rd_Chan <= CH_Select; |
RAM_Rd_Ptr <= SPN_Pointers(i); |
AVG_Ctl <= ADV_PTR; |
|
when ADV_PTR => |
SP0_Pointers(i) <= SP0_Pointers(i) + 1; |
AVG_Ctl <= CALC_NEXT; |
|
when CALC_NEXT => |
Accumulators(i) <= Accumulators(i) + |
unsigned( Data_New ) - |
unsigned( Data_Old ); |
AVG_Ctl <= WR_NEW; |
|
when WR_NEW => |
RAM_Wr_Chan <= CH_Select; |
RAM_Wr_Ptr <= SP0_Pointers(i); |
RAM_Wr_Data <= Data_New; |
RAM_Wr_En <= '1'; |
SPN_Pointers(i) <= SP0_Pointers(i) + 1; |
Interrupt <= '1'; |
AVG_Ctl <= IDLE; |
|
when others => |
null; |
end case; |
|
end if; |
end process; |
|
U_BUFF : entity work.mavg_buffer_16b |
port map( |
clock => Clock, |
data => RAM_Wr_Data, |
rdaddress => RAM_Rd_Addr, |
wraddress => RAM_Wr_Addr, |
wren => RAM_Wr_En, |
q => RAM_Rd_Data |
); |
|
end architecture; |
/VHDL/threshold_comp.vhd
0,0 → 1,205
-- Copyright (c)2022 Jeremy Seth Henry |
-- All rights reserved. |
-- |
-- Redistribution and use in source and binary forms, with or without |
-- modification, are permitted provided that the following conditions are met: |
-- * Redistributions of source code must retain the above copyright |
-- notice, this list of conditions and the following disclaimer. |
-- * Redistributions in binary form must reproduce the above copyright |
-- notice, this list of conditions and the following disclaimer in the |
-- documentation and/or other materials provided with the distribution, |
-- where applicable (as part of a user interface, debugging port, etc.) |
-- |
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``AS IS'' AND ANY |
-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
-- DISCLAIMED. IN NO EVENT SHALL JEREMY SETH HENRY BE LIABLE FOR ANY |
-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
-- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
-- |
-- VHDL Units : threshold_comp |
-- Description: Compares the value on Port_A to either Port_B or a fixed, |
-- : unsigned constant with hysteresis. |
-- |
-- Revision History |
-- Author Date Change |
------------------ -------- --------------------------------------------------- |
-- Seth Henry 06/29/22 Initial import |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.std_logic_unsigned.all; |
use ieee.std_logic_arith.all; |
|
entity threshold_comp is |
generic( |
Constant_B : boolean := TRUE; |
Hysteresis : integer := 2; |
Threshold : integer := 0; |
Bus_Width : integer := 16; |
Reset_Level : std_logic := '1' |
); |
port( |
Clock : in std_logic; |
Reset : in std_logic; |
-- |
Port_A_Data : in std_logic_vector(Bus_Width - 1 downto 0); |
Port_A_Update : in std_logic; |
Port_B_Data : in std_logic_vector(Bus_Width - 1 downto 0) := (others => '0'); |
Port_B_Update : in std_logic := '0'; |
-- |
A_GT_B : out std_logic; |
A_LTE_B : out std_logic |
); |
end entity; |
|
architecture behave of threshold_comp is |
|
function limit_int (x : in integer; y : in integer ) return integer is |
variable upper : integer; |
variable retval : integer; |
begin |
upper := (2**y) - 1; |
|
if( x <= 0 )then |
retval := 0; |
elsif( x > upper )then |
retval := upper; |
else |
retval := x; |
end if; |
return retval; |
end function; |
|
constant Int_Width : integer := Bus_Width + 1; |
|
constant Comp_Val_r_i : integer := Threshold + Hysteresis; |
constant Comp_Val_r_l : integer := limit_int(Comp_Val_r_i, Int_Width); |
constant Comp_Val_r : std_logic_vector(Bus_Width downto 0) := |
conv_std_logic_vector(Comp_Val_r_l, Int_Width); |
|
constant Comp_Val_f_i : integer := Threshold - Hysteresis; |
constant Comp_Val_f_l : integer := limit_int(Comp_Val_f_i, Int_Width); |
constant Comp_Val_f : std_logic_vector(Bus_Width downto 0) := |
conv_std_logic_vector(Comp_Val_f_l, Int_Width); |
|
signal Hist_Dir : std_logic := '0'; |
signal Update_q : std_logic := '0'; |
signal Update_qq : std_logic := '0'; |
|
signal Port_A_q : std_logic_vector(Bus_Width downto 0) := |
(others => '0'); |
|
signal Port_B_r : std_logic_vector(Bus_Width downto 0) := |
(others => '0'); |
|
signal Port_B_f : std_logic_vector(Bus_Width downto 0) := |
(others => '0'); |
|
signal Port_A_LT_B : std_logic := '0'; |
|
begin |
|
Constant_Port_B : if( Constant_B )generate |
|
Compare_proc: process( Clock, Reset ) |
begin |
if( Reset = Reset_Level )then |
Update_q <= '0'; |
Hist_Dir <= '1'; |
Port_A_q <= (others => '0'); |
A_GT_B <= '0'; |
A_LTE_B <= '1'; |
elsif( rising_edge(Clock) )then |
|
-- Update the internal registers on Update, then start a short shift |
-- register to pipeline the logic. |
Update_q <= Port_A_Update; |
if( Port_A_Update = '1' )then |
Port_A_q <= '0' & Port_A_Data; |
end if; |
|
-- Update the output on q |
if( Update_q = '1' )then |
if( Hist_Dir = '1' and Port_A_q >= Comp_Val_r )then |
Hist_Dir <= '0'; |
A_GT_B <= '1'; |
A_LTE_B <= '0'; |
end if; |
if( Hist_Dir = '0' and Port_A_q <= Comp_Val_f )then |
Hist_Dir <= '1'; |
A_GT_B <= '0'; |
A_LTE_B <= '1'; |
end if; |
end if; |
|
end if; |
end process; |
|
end generate; |
|
Variable_Port_B : if( not Constant_B )generate |
|
Compare_proc: process( Clock, Reset ) |
begin |
if( Reset = Reset_Level )then |
Update_q <= '0'; |
Update_qq <= '0'; |
Hist_Dir <= '1'; |
Port_A_q <= (others => '0'); |
Port_B_r <= (others => '0'); |
Port_B_f <= (others => '0'); |
A_GT_B <= '0'; |
A_LTE_B <= '1'; |
elsif( rising_edge(Clock) )then |
|
-- Update the internal registers on Update, then start a short shift |
-- register to pipeline the logic. |
Update_q <= Port_A_Update or Port_B_Update; |
|
if( Port_A_Update = '1' )then |
Port_A_q <= '0' & Port_A_Data; |
end if; |
|
if( Port_B_Update = '1' )then |
Port_B_r <= ('0' & Port_B_Data) + Hysteresis; |
Port_B_f <= ('0' & Port_B_Data) - Hysteresis; |
end if; |
|
-- Clip the upper and lower thresholds so that the comparator can't get |
-- stuck and will trip on equal. |
Update_qq <= Update_q; |
|
if( Port_B_R(Bus_Width) = '1' )then |
Port_B_R(Bus_Width) <= '0'; |
Port_B_R(Bus_Width - 1 downto 0) <= (others => '1'); |
end if; |
|
if( Port_B_F(Bus_Width) = '1' )then |
Port_B_F <= (others => '0'); |
end if; |
|
-- Update the output on qq to give the clipping logic a chance to update |
if( Update_qq = '1' )then |
if( Hist_Dir = '1' and Port_A_q >= Port_B_r )then |
Hist_Dir <= '0'; |
A_GT_B <= '1'; |
A_LTE_B <= '0'; |
end if; |
if( Hist_Dir = '0' and Port_A_q <= Port_B_f )then |
Hist_Dir <= '1'; |
A_GT_B <= '0'; |
A_LTE_B <= '1'; |
end if; |
end if; |
|
end if; |
end process; |
|
end generate; |
|
end architecture; |