Still newbie question : Dialog between states machines.

F

Fred Bartoli

Guest
Hello,

I have 2 fsm that are clocked on different and asynchronous clocks.

The first INSTR_fsm (clocked on CPLD_EXEC) executes some commands issued on
a serial bus. Basically it updates different registers.

The second GATING_fsm (clocked on Ck1600kHz) drives some signals for a fast
acquisition process.

In GATING_fsm I have a Hold_St where the acquired value is held and I wait
for the measurement to be completed by an external ADC (slow) before going
to the StartIntegrate_St or StartGate_St state for another measurement
cycle.
The state transition to StartIntegrate_St or StartGate_St is triggered by a
software acknowledge sent to the INSTR_fsm.
So basically what I have to do is have a MeasReaf flag ff that is set by the
GATING_fsm when entering the Hold_St state, used as a condition still in
GATING_fsm and is reset in CMD_fsm on a special command.

How can I do that in behavioral description ? I mean I'm an hardware guy
and I've tried hard to stay away from what is my first inclination, i.e.
thinking in hardware, so as to have good code clarity...

Oh, strong constrain is the final CPLD size : I can only afford 64MC thanks
to package size.

I also welcome any comment/suggestion about the programming style...

Thanks very much for your time.

Fred.


Here's the code :



-- **************************************************************
-- Serial communication register
-- **************************************************************
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY Ser2Par is
generic ( WIDTH : integer := 8 );
port (
Clk : in std_logic;
SerIn : in std_logic;
ParOut : out std_logic_vector ( WIDTH-1 downto 0);
SerOut : out std_logic
);
END Ser2Par;

ARCHITECTURE behv of Ser2Par is
constant clockEdge : std_logic := '1';
signal Q : std_logic_vector(WIDTH-1 downto 0);

BEGIN
--------------------------------------------------------------------------
----
shr: process(Clk)
--------------------------------------------------------------------------
----
begin
if (Clk = clockEdge and Clk'event) then
Q <= SerIn & Q(WIDTH-1 downto 1);
end if;
end process shr;
--------------------------------------------------------------------------
----
ParOut <= Q;
SerOut <= Q(0);
END behv;


-- **************************************************************
-- Gating counter
-- **************************************************************
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY PRBS_10 is
Port (
Clk : in std_logic;
Load : in std_logic;
Input : in std_logic_vector (9 downto 0);
EndCount: out std_logic );
END PRBS_10;

ARCHITECTURE behv of PRBS_10 is
signal Q: std_logic_vector (9 downto 0);
BEGIN
--------------------------------------------------------------------------
----
counter: process(Clk, Load, Input)
--------------------------------------------------------------------------
----
variable Count : std_logic_vector (9 downto 0);
begin
if Load = '1' then
Count := Input ;
elsif (Clk='0' and Clk'event) then
Count := Q(8 downto 0) & (Q(9) xor Q(6)) ;
end if;
Q <= Count;
end process counter;
--------------------------------------------------------------------------
----
EndCount <= '1' when Q = b"1111111111" else '0';
END behv;


-- **************************************************************
-- Clock predivider
-- **************************************************************
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY Ck100K_div is
Port (
Clk : In std_logic;
Rst : In std_logic;
CkDiv : Out std_logic
);
END Ck100K_div;

ARCHITECTURE behavior of Ck100K_div is
BEGIN
--------------------------------------------------------------------------
----
counter: process(Clk, Rst)
--------------------------------------------------------------------------
----
variable Count: Natural range 0 to 15;
begin
if (Rst = '1') then
count := 0;
elsif falling_edge(Clk) then -- Count is advanced by 1/2 clock cycle to
account for the
count := (count+1) mod 16; -- gating state machine 1 cycle delay when
stopping counting.
end if;

if (Count >= 8) then
CkDiv <= '1';
else
CkDiv <= '0';
end if;
end process counter;
--------------------------------------------------------------------------
----
END behavior;


-- **************************************************************
-- CPLD instance
-- **************************************************************
--library metamor;
--use metamor.attributes.all;
LIBRARY ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

ENTITY CPLD is
port (
Ck_1600KHz : in std_logic := '0';
GATE_TRIG : in std_logic := '0';
CPLD_EXEC : in std_logic := '0';
SER_DATA : in std_logic := '0';
SER_CLK : in std_logic := '0';


CPLD_DATA : out std_logic;
CPU_REQ : out std_logic;
CalGen_Clk : out std_logic;
CalGen_Dat : out std_logic;
PRESET_ENABLE : out std_logic;
nCOS_HOLD_P : out std_logic;
nCOS_HOLD_M : out std_logic;
nSIN_HOLD_P : out std_logic;
nSIN_HOLD_M : out std_logic;
nIF_SAMPLE : out std_logic;

TEMP_A : out std_logic_vector(2 downto 0);
PRESET_A : out std_logic_vector(1 downto 0)
);

-- attribute pinnum of Ck_1600KHz : signal is "37";
-- attribute pinnum of GATE_TRIG : signal is "12";
-- attribute pinnum of CPLD_EXEC : signal is "2";
-- attribute pinnum of SER_DATA : signal is "11";
-- attribute pinnum of SER_CLK : signal is "10";
--
-- attribute pinnum of CPLD_DATA : signal is "3";
-- attribute pinnum of CPU_REQ : signal is "5";
-- attribute pinnum of CalGen_Clk : signal is "33";
-- attribute pinnum of CalGen_Dat : signal is "34";
-- attribute pinnum of PRESET_ENABLE: signal is "19";
-- attribute pinnum of nCOS_HOLD_P : signal is "18";
-- attribute pinnum of nCOS_HOLD_M : signal is "22";
-- attribute pinnum of nSIN_HOLD_P : signal is "23";
-- attribute pinnum of nSIN_HOLD_M : signal is "25";
-- attribute pinnum of nIF_SAMPLE : signal is "27";
--
-- attribute pinnum of TEMP_A : signal is "31,30,28";
-- attribute pinnum of PRESET_A : signal is "21,20";

END CPLD;

ARCHITECTURE structure of CPLD is
use work.all;
use work.IF_CPLD_types.all;

signal GateLoadBus : std_logic_vector ( 9 downto 0);
signal SerComBus : op;
alias SerComParm1 : std_logic is SerComBus(0);
alias SerComParm1_2 : std_logic is SerComBus(1);
alias SerComParm1_3 : std_logic is SerComBus(2);
alias SerComParm2 : std_logic_vector (1 downto 0) is SercomBus (1 downto
0);
alias SerComParm3 : std_logic_vector (2 downto 0) is SercomBus (2 downto
0);
signal Internal_100kHz_Clk : std_logic; -- internal 100KHz clock
signal CalEnable : std_logic;
signal GatingEnable : std_logic;
signal GateCounterLoad : std_logic;
signal GateEnd : std_logic; -- Pulses when Gatecounter ends.
signal PresetOn : std_logic; -- PresetEnable default states
signal PresetMode : std_logic;
signal PresetGating : std_logic; --
signal RstCalDiv : std_logic; -- Resets the 100kHz clock divider

signal SinHold_P : std_logic;
signal SinHold_M : std_logic;
signal CosHold_P : std_logic;
signal CosHold_M : std_logic;
signal IFsample : std_logic;
signal Integrate : std_logic;
signal MeasRead : std_logic;

type GatingStates is (StartIntegrate_St, StartGate_St, Hold_St,
WaitTrig_St, Count_St);
signal GatingState : GatingStates;


BEGIN
nSIN_HOLD_P <= not SinHold_P;
nSIN_HOLD_M <= not SinHold_M;
nCOS_HOLD_P <= not CosHold_P;
nCOS_HOLD_M <= not CosHold_M;
nIF_SAMPLE <= IFsample;


-- *************************************
-- Microcontroler interface
SerialComm: entity Ser2Par port map ( Clk=>SER_CLK, SerIn=>SER_DATA,
ParOut=>SerComBus, SerOut=>CPLD_DATA);


-- *************************************
-- Gating system
GateCounter: entity PRBS_10 port map (Clk=>Internal_100kHz_Clk ,
Load=>GateCounterLoad, Input=>GateLoadBus, EndCount=> GateEnd);


-- *************************************
-- Dividing and synchronizing the 1.6MHz clock for calibration generator and
Gate counter clocking.
CalClk_div: entity Ck100K_div port map (Clk=>Ck_1600KHz, Rst=> RstCalDiv,
CkDiv=>Internal_100kHz_Clk );
CalGen_Dat <= CalEnable and Internal_100kHz_Clk;
CalGen_Clk <= NOT Ck_1600KHz;


-- *************************************
-- Instruction process state machine
--------------------------------------------------------------------------
----
instr_fsm : process (CPLD_EXEC)
--------------------------------------------------------------------------
----
type Instr_state is (InstrFetch_St,LoadGateCountFetch_St);
variable Instr_St : Instr_state;
variable CurrentOp : op;

begin
if rising_edge(CPLD_EXEC) then
CurrentOp := SerComBus and op_mask;

-- op_LoadGateCount
case Instr_St is
when InstrFetch_St =>
case CurrentOp is
when op_LoadGateCount =>
Instr_St := LoadGateCountFetch_St;
GateLoadBus (9 downto 8) <= SerComParm2;

when op_LoadTempChannel =>
TEMP_A <= SerComParm3;

when op_LoadPresetChannel =>
PRESET_A <= SerComParm2;
PresetOn <= SerComParm1_3;

when op_EnablePresetMode =>
-- PresetMode <= SerComParm1_2;

when op_ReadStatus =>
null;

when op_EnableCalibration =>
CalEnable <= SerComParm1;

when op_EnableGating =>
GatingEnable <= SerComParm1;

when op_MeasRead =>
MeasRead <= '1';

when op_Integrate =>
Integrate <= SerComParm1;

when others =>
null;

end case;

when LoadGateCountFetch_St =>
Instr_St := InstrFetch_St;
GateLoadBus (7 downto 0) <= SerCombus;

end case;
end if;
end process;



-- *************************************
-- Gating handling state machine
--------------------------------------------------------------------------
----
Gating_fsm: process (Ck_1600KHz, GatingEnable, CalEnable)
--------------------------------------------------------------------------
----
-- type GatingStates is (StartIntegrate_St, StartGate_St, Hold_St,
WaitTrig_St, Count_St);
variable Gating_St : GatingStates;
-- variable GatingState : GatingStates;
variable Sampling : std_logic;
variable xHold : std_logic;

begin
if (GatingEnable = '0') then -- (continuous sampling mode)
Gating_St := StartIntegrate_St;
RstCalDiv <= not CalEnable;
GateCounterLoad <= '0';
-- PresetGating <= PresetOn; --'0';
PRESET_ENABLE <= PresetOn;

SinHold_P <= '0'; -- Averaging mode for all the outputs
SinHold_M <= '0';
CosHold_P <= '0';
CosHold_M <= '0';
IFSample <= '1'; -- (continuous sampling)

elsif rising_edge(Ck_1600KHz) then -- (gated sampling mode)
case Gating_St is
when StartIntegrate_St =>
if GATE_TRIG = '1' then
Gating_St := WaitTrig_St;
end if;

when StartGate_St =>
if GATE_TRIG = '1' then
Gating_St := WaitTrig_St;
end if;

when WaitTrig_St =>
if GATE_TRIG = '0' then
Gating_St := Count_St;
end if;

when Count_St =>
if GateEnd = '1' then
Gating_St := Hold_St;
end if;

when Hold_St =>
if MeasRead = '1' then
if Integrate = '1' then
Gating_St := StartIntegrate_St;
else
Gating_St := StartGate_St;
end if;
end if;

when others =>
Gating_St := StartIntegrate_St;
end case;

case Gating_St is
when StartIntegrate_St =>
GateCounterLoad <= '1'; -- Load the PRBS counter
RstCalDiv <= '1'; -- Stop the 100kHz gate counter clock
Sampling := '0';
PRESET_ENABLE <= '0';
xHold := '0';
MeasRead <= '0';
-- PresetGating <= '1';

when StartGate_St =>
GateCounterLoad <= '1'; -- Load the PRBS counter
RstCalDiv <= '1'; -- Stop the 100kHz gate counter clock
Sampling := '0';
PRESET_ENABLE <= '1';
xHold := '1';
MeasRead <= '0';
-- PresetGating <= '1';

when WaitTrig_St =>
GateCounterLoad <= '0'; -- Release it and wait for trigger
Sampling := '0';
PRESET_ENABLE <= '0';
xHold := '1';
-- PresetGating <= '0';

when Count_St =>
RstCalDiv <= '0'; -- Allow internal 100kHz clock for gate count
Sampling := '1';

when Hold_St =>
GateCounterLoad <= '1'; -- Load the PRBS counter
RstCalDiv <= '1'; -- Stop the 100kHz gate counter clock
Sampling := '0';
PRESET_ENABLE <= '1';
xHold := '1';
-- PresetGating <= '1';

end case;
SinHold_P <= Sampling; -- Average mode for all the outputs during gate
sampling
SinHold_M <= Sampling;
CosHold_P <= Sampling;
CosHold_M <= Sampling;
IFSample <= not Sampling; -- (Sample)

GatingState <= GatingStates'val(GatingStates'pos(Gating_St)); -- Just to
show correct SM states
end if;
end process;

--PRESET_ENABLE <= (PresetGating and GatingEnable) or ((not GatingEnable)
and PresetOn) ;

END structure;
 
Fred Bartoli wrote:

I have 2 fsm that are clocked on different and asynchronous clocks.
.. . .
How can I do that in behavioral description ? I mean I'm an hardware guy
and I've tried hard to stay away from what is my first inclination, i.e.
thinking in hardware, so as to have good code clarity...
It is possible to do both.
I could tell you more if you post your package IF_CPLD_types.
Is this working code, or a first cut?

Your biggest need is not code clarity, but synchronization.
Consider using a single entity CPLD containing two processes:
serial: process(reset, SER_CLK) and
parallel: process(reset, Ck_1600KHz)

All of your other top "clocks" become
inputs to one of these processes.

-- Mike Treseler
 
"Mike Treseler" <tres@fluke.com> a écrit dans le message de news:
101laja2vhal0a0@corp.supernews.com...
Fred Bartoli wrote:

I have 2 fsm that are clocked on different and asynchronous clocks.
. . .
How can I do that in behavioral description ? I mean I'm an hardware
guy
and I've tried hard to stay away from what is my first inclination, i.e.
thinking in hardware, so as to have good code clarity...

It is possible to do both.
I could tell you more if you post your package IF_CPLD_types.
Is this working code, or a first cut?

Your biggest need is not code clarity, but synchronization.
Consider using a single entity CPLD containing two processes:
serial: process(reset, SER_CLK) and
parallel: process(reset, Ck_1600KHz)

All of your other top "clocks" become
inputs to one of these processes.

-- Mike Treseler

Hello Mike,

thanks for the input.

No this code is not a (still) working code but I hope to succeed soon. That
one was a first cut and also my very first attempt to code in VHDL, hence
all the "errors". This is for a small (64MC) Atmel CPLD that I first wanted
to code with their demo tools (wincupl then prochip designer cupl) but both
proved to be the worst useless engineering bugware joke I've ever seen.
Fortunately the prochipdes VHDL compiler seems to work OK and the demo
version doesn't time out and is just OK for my project size so I'm using it
with Sonata for code development and simulation (with also some annoying
bugs but nothing is perfect). That's probably not very optimal but it's for
a pretty huge homebrewed project and all my money has to go to the hardware
so it's OK to me.


I was not sure what you meant by :
Consider using a single entity CPLD containing two processes:
serial: process(reset, SER_CLK) and
parallel: process(reset, Ck_1600KHz)
and I had a lot of questions but I decided first to blindly follow your
advice hoping it'll shed some light and indeed it did to a great extend.

I then have tried lots of coding options, like where to hang the PRBS and
CkDivcounter so as to minimize the hardware..., and the one I ended with
reaches the smallest size I can achieve.
I've enclosed it at the end. Will you care having a look at it and telling
me wether it's what you had in thought ?
Is there some (obvious or maybe less obvious) way to further reduce the
logic ? (I still have to add some features and don't want to grow in CPLD
size).

As per yourequest I've also added the IF_CPLD_types package but it has
nothing special : just opcodes definitions.

Thanks again for your help.
Fred.



-- **************************************************************
-- CPLD instance
-- **************************************************************
--library metamor;
--use metamor.attributes.all;
LIBRARY ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

ENTITY CPLD is
port (
Ck_1600KHz : in std_logic := '0';
GATE_TRIG : in std_logic := '0';
CPLD_EXEC : in std_logic := '0';
SER_DATA : in std_logic := '0';
SER_CLK : in std_logic := '0';


CPLD_DATA : out std_logic;
CPU_REQ : out std_logic;
CalGen_Clk : out std_logic;
CalGen_Dat : out std_logic;
PRESET_ENABLE : out std_logic;
nCOS_HOLD_P : out std_logic;
nCOS_HOLD_M : out std_logic;
nSIN_HOLD_P : out std_logic;
nSIN_HOLD_M : out std_logic;
nIF_SAMPLE : out std_logic;

TEMP_A : out std_logic_vector(2 downto 0);
PRESET_A : out std_logic_vector(1 downto 0)
);

-- attribute pinnum of Ck_1600KHz : signal is "37";
-- attribute pinnum of GATE_TRIG : signal is "12";
-- attribute pinnum of CPLD_EXEC : signal is "2";
-- attribute pinnum of SER_DATA : signal is "11";
-- attribute pinnum of SER_CLK : signal is "10";
--
-- attribute pinnum of CPLD_DATA : signal is "3";
-- attribute pinnum of CPU_REQ : signal is "5";
-- attribute pinnum of CalGen_Clk : signal is "33";
-- attribute pinnum of CalGen_Dat : signal is "34";
-- attribute pinnum of PRESET_ENABLE: signal is "19";
-- attribute pinnum of nCOS_HOLD_P : signal is "18";
-- attribute pinnum of nCOS_HOLD_M : signal is "22";
-- attribute pinnum of nSIN_HOLD_P : signal is "23";
-- attribute pinnum of nSIN_HOLD_M : signal is "25";
-- attribute pinnum of nIF_SAMPLE : signal is "27";
--
-- attribute pinnum of TEMP_A : signal is "31,30,28";
-- attribute pinnum of PRESET_A : signal is "21,20";

END CPLD;

ARCHITECTURE structure of CPLD is
use work.all;
use work.IF_CPLD_types.all;

-- signal GateLength : std_logic_vector ( 9 downto 0);
signal SerCom : op;
alias Opcode : op is SerCom;
alias Param1 : std_logic is Opcode(0);
alias Param1_2 : std_logic is Opcode(1);
alias Param1_3 : std_logic is Opcode(2);
alias Param2 : std_logic_vector (1 downto 0) is Opcode(1 downto 0);
alias Param3 : std_logic_vector (2 downto 0) is Opcode(2 downto 0);
alias Param8 : std_logic_vector (7 downto 0) is Opcode;
signal Internal_100kHz_Clk : std_logic; -- internal 100KHz clock
signal CalEnable : std_logic;
signal GatingEnable : std_logic;
signal PresetOn : std_logic; -- PresetEnable default states
-- signal PresetMode : std_logic;
-- signal PresetGating : std_logic; --
-- signal RstCalDiv : std_logic; -- Resets the 100kHz clock divider

signal SinHold_P : std_logic;
signal SinHold_M : std_logic;
signal CosHold_P : std_logic;
signal CosHold_M : std_logic;
signal IFsample : std_logic;
signal Integrate : std_logic;
-- signal HoldMeas : std_logic;

signal PRBS_sig : std_logic_vector(9 downto 0);
-- signal PRBS_Count : std_logic_vector(9 downto 0);

type GatingStates is (StartIntegrate_St, StartGate_St, Hold_St,
WaitTrig_St, Count_St);
signal GatingState : GatingStates;


BEGIN


-- *************************************
-- Microcontroler interface
--------------------------------------------------------------------------
----
CPLD_DATA <= SerCom(0);

shr: process(SER_CLK)
--------------------------------------------------------------------------
----
begin
if rising_edge(SER_CLK) then
SerCom <= SER_DATA & SerCom(7 downto 1);
end if;
end process shr;
--------------------------------------------------------------------------
----




-- *************************************
-- Dividing and synchronizing the 1.6MHz clock for calibration generator
and Gate counter clocking.
CalGen_Dat <= Internal_100kHz_Clk when CalEnable = '1' else '0' ;
CalGen_Clk <= NOT Ck_1600KHz;




--------------------------------------------------------------------------
----
Parallel: process (Ck_1600KHz, CPLD_EXEC, Internal_100kHz_Clk, PresetOn,
CalEnable, GatingEnable)
--------------------------------------------------------------------------
----
type Instr_state is (InstrFetch_St, LoadGateCountFetch_St);
variable Instr_St : Instr_state;
variable CurrentOp : op;

-- type GatingStates is (StartIntegrate_St, StartGate_St, Hold_St,
WaitTrig_St, Count_St);
variable Gating_St : GatingStates;
variable Sampling : std_logic;
variable xHold : std_logic;
variable CkDivCount : Natural range 0 to 15;
variable PRBS_Count : std_logic_vector(9 downto 0);
variable GateEnd : std_logic; -- Pulses when Gatecounter ends.
-- variable GateCounterLoad : std_logic;
variable HoldMeas : std_logic;
variable AllowPreset : std_logic;
variable GateLength : std_logic_vector ( 9 downto 0);
begin

-- *************************************
-- Instruction process state machine
--------------------------------------------------------------------------
----
if rising_edge(CPLD_EXEC) then
CurrentOp := Opcode and op_mask;

case Instr_St is
when InstrFetch_St =>
case CurrentOp is
when op_LoadGateCount =>
Instr_St := LoadGateCountFetch_St;
GateLength (9 downto 8) := Param2;

when op_LoadTempChannel =>
TEMP_A <= Param3;

when op_LoadPresetChannel =>
PRESET_A <= Param2;
PresetOn <= Param1_3;

when op_EnablePresetMode =>
-- PresetMode <= Param1_2;

when op_ReadStatus =>
null;

when op_EnableCalibration =>
CalEnable <= Param1;

when op_EnableGating =>
GatingEnable <= Param1;

when op_MeasRead =>
HoldMeas := '0'; -- Clear the flag after software acquired the Meas.

when op_Integrate =>
Integrate <= Param1;

when others =>
null;

end case;

when LoadGateCountFetch_St =>
Instr_St := InstrFetch_St;
GateLength (7 downto 0) := Param8;

when others =>
null;

end case;

end if;
--------------------------------------------------------------------------
----



-- *************************************
-- 100KHz clock predivider
if falling_edge(Ck_1600KHz) then
if Gating_St = WaitTrig_St then
CkDivcount := 0;
else
CkDivCount := (CkDivCount+1) mod 16;
end if;
end if;
if (CkDivCount >= 8) then
Internal_100kHz_Clk <= '1';
else
Internal_100kHz_Clk <= '0';
end if;


-- *************************************
-- Gating handling state machine
--------------------------------------------------------------------------
----
if (GatingEnable = '0') then -- (continuous sampling mode)
Gating_St := StartIntegrate_St;
AllowPreset := '1';
HoldMeas := '0';
Sampling := '1';
PRBS_Count := (others=>'0');

GatingState <= Gating_St;
xHold := '0';
SinHold_P <= '0'; -- Averaging mode for all the outputs
SinHold_M <= '0';
CosHold_P <= '0';
CosHold_M <= '0';
IFSample <= '1'; -- (continuous sampling)

elsif rising_edge(Ck_1600KHz) then -- (gated sampling mode)

-- CkDivCount := (CkDivCount+1) mod 16;

case Gating_St is
when StartIntegrate_St =>
if GATE_TRIG = '1' then
Gating_St := WaitTrig_St;
end if;

when StartGate_St =>
if GATE_TRIG = '1' then
Gating_St := WaitTrig_St;
end if;

when WaitTrig_St =>
if GATE_TRIG = '0' then
Gating_St := Count_St;
end if;

when Count_St =>
if PRBS_Count = b"1111111111" then
Gating_St := Hold_St;
end if;

when Hold_St =>
if HoldMeas = '0' then -- Meas has been done and the CPU cleared the
flag.
if Integrate = '1' then
Gating_St := StartIntegrate_St;
else
Gating_St := StartGate_St;
end if;
end if;

when others =>
null;
end case;

case Gating_St is
when StartIntegrate_St =>
Sampling := '0';
xHold := '0';
AllowPreset := '0';

when StartGate_St =>
Sampling := '0';
xHold := '1';
AllowPreset := '1';

when WaitTrig_St =>
-- CkDivCount := 15; -- Resets the 100kHz gate counter clock (better
done outside)
PRBS_Count := GateLength ; -- Initialize gate length
Sampling := '0';
xHold := '1';
AllowPreset := '0';

when Count_St =>
if CkDivCount = 15 then -- counts one IF clock cycle
PRBS_Count := PRBS_Count(8 downto 0) & (PRBS_Count(9) xor
PRBS_Count(6)) ;
end if;
Sampling := '1';
xHold := Integrate;

when Hold_St =>
HoldMeas := '1'; -- Holds the result until software clears the flag
Sampling := '0';
xHold := '1';
AllowPreset := '0';

when others =>
null;
end case;

SinHold_P <= xHold; -- Average mode for all the outputs during gate
sampling
SinHold_M <= xHold;
CosHold_P <= xHold;
CosHold_M <= xHold;
IFSample <= Sampling; -- (Sample)

GatingState <= GatingStates'val(GatingStates'pos(Gating_St)); -- Just to
show correct SM states
end if;
--------------------------------------------------------------------------
----

PRESET_ENABLE <= (AllowPreset and GatingEnable) or ((not GatingEnable) and
PresetOn) ;

end process;

nSIN_HOLD_P <= not SinHold_P;
nSIN_HOLD_M <= not SinHold_M;
nCOS_HOLD_P <= not CosHold_P;
nCOS_HOLD_M <= not CosHold_M;
nIF_SAMPLE <= not IFsample;


END structure;



******************************************************
******************************************************


package IF_CPLD_types is

subtype op is std_logic_vector(7 downto 0);
constant op_mask : op := X"F8";



constant op_LoadTempChannel : op := X"00"; -- 0 0 O 0 0 T2 T1 T0
-- with T2 T1 T0 : Temp channel to select
constant op_LoadPresetChannel : op := X"10"; -- 0 0 O 1 0 En P1 P0
-- with P1 P0 : Preset channel to select
constant op_EnablePresetMode : op := X"20"; --
constant op_LoadGateCount : op := X"30"; -- 0 0 1 1 0 0 G9 G8
-- G7 G6 G5 G4 G3 G G1 G0
constant op_ReadStatus : op := X"40";
constant op_EnableCalibration : op := X"50"; -- 0 1 O 1 0 0 0 En
constant op_EnableGating : op := X"60"; -- 0 1 1 0 0 0 0 En
constant op_PresetCal : op := X"70"; -- 0 1 1 1 0 0 C1 C0
-- C1 C0 : channel to calibrate
constant op_MeasRead : op := X"80"; -- Clear HoldMeasure flag
constant op_Integrate : op := X"90"; -- 1 0 O 1 0 0 0 En
-- constant op_ : op := X"";
-- constant op_ : op := X"";
-- constant op_ : op := X"";
-- constant op_ : op := X"";
-- constant op_ : op := X"";
-- constant op_ : op := X"";
-- constant op_ : op := X"";

end IF_CPLD_types;

package body IF_CPLD_types is
end IF_CPLD_types;
 
Fred Bartoli wrote:

I was not sure what you meant by :

Consider using a single entity CPLD containing two processes:
serial: process(reset, SER_CLK) and
parallel: process(reset, Ck_1600KHz)
All wires crossing clock domains must be synchronized.
Limiting the design to two domains will minimize this problem.
Best case would be running everything on the fastest clock.
Limiting the number of processes is just style.
I prefer one-liners for counters, shifters etc.


I've enclosed it at the end. Will you care having a look at it and telling
me wether it's what you had in thought ?
OK, I'll scan thru here.
It looks to me like you've paid some dues.
When you have a working testbench, I'll look again.


Is there some (obvious or maybe less obvious) way to further reduce the
logic ? (I still have to add some features and don't want to grow in CPLD
size).
Don't worry about that until you have a working sim.
It's easier to chop things out when you can tell
if your code is broken or not.

As per yourequest I've also added the IF_CPLD_types package but it has
nothing special : just opcodes definitions.
Well, it allowed me to run a sim compile.
The reason this:
GatingState := GatingStates'val(GatingStates'pos(Gating_St));

didn't compile was your use of <= instead of :=
-- **************************************************************
-- CPLD instance
-- **************************************************************
--library metamor;
--use metamor.attributes.all;
LIBRARY ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
well done.


shr: process(SER_CLK)
can't afford a reset?

--------------------------------------------------------------------------
----
Parallel: process (Ck_1600KHz, CPLD_EXEC, Internal_100kHz_Clk, PresetOn,
CalEnable, GatingEnable)
I thought Ck_1600KHz was your system clock. This should be

Parallel: process (CPLD_EXEC);

to match > if rising_edge(CPLD_EXEC) then



-- *************************************
-- 100KHz clock predivider
if falling_edge(Ck_1600KHz) then
if Gating_St = WaitTrig_St then

No. you only get one edge per process.
Where does Ck_1600KHz : in std_logic come from?

-- Mike Treseler
 
Thanks again Mike,
Comments, answers and questions follows.


"Mike Treseler" <tres@fluke.com> a écrit dans le message de news:
4022D568.6080207@fluke.com...

Consider using a single entity CPLD containing two processes:
serial: process(reset, SER_CLK) and
parallel: process(reset, Ck_1600KHz)

All wires crossing clock domains must be synchronized.
Yep, metastability and all that stuff... but in my case the very short
resources of the CPLD made me think twice to this and the cost of 2 FFs is
high in my case. I finally choosed to carefully examine and assume the
consequences of a metastable event. These could only occur on special
occasions and will (should), at worst, just imply one Ck_1600kHz cycle delay
which will be of no consequence when arriving. This of course unless I've
overlooked something important.


Limiting the design to two domains will minimize this problem.
Best case would be running everything on the fastest clock.
Limiting the number of processes is just style.
I agree and the mess of my first attempt came from that I first implemented
the hardware I new I needed, then tried to connect them... In fact I think I
was trying to use VHDL like ABEL, CUPL. Of course not the right way but I
must say that my domain of expertise is analog and I just did CPLDs on
occasions, to debug someone else production.


<>
When you have a working testbench, I'll look again.
I didn't expected you'll go to the trouble of running the code. That's why I
have not included the testbench which I have "developped" with the CPLD
code. In fact this is more a quick and dirty environment that evolved with
the needs of the CPLD portion to be tested than something with a rigorous
structure and doesn't respond to any industrial needs, i.e. I just have
inputs generation and no output tests for example (too much trouble at this
time).
Anyway, I'll include it at the end, and also the commands input file for it.


Is there some (obvious or maybe less obvious) way to further reduce the
logic ? (I still have to add some features and don't want to grow in
CPLD
size).

Don't worry about that until you have a working sim.
It's easier to chop things out when you can tell
if your code is broken or not.
I have a working sim, and also a great concern about the size : I already
have the board designed, have to know wether the chossen package size is OK,
and also have to balance some functionnalities between the CPLD and a small
onboard uC that'll interpret some high level commands from the main CPU and
translate them to a list of low level commands for the CPLD.
I also feel a strong need to learn in any aspect of good VHDL coding as I
know that my design will probably just fit with the 64MC and I don't want to
restart coding again and again... This is may look slower at the beginning
but will make me win some time later. I'll also have other CPLDs to develop,
.... and the main reason is probably that I've always done like this with
good fortune, so...


As per yourequest I've also added the IF_CPLD_types package but it has
nothing special : just opcodes definitions.

Well, it allowed me to run a sim compile.
The reason this:
GatingState := GatingStates'val(GatingStates'pos(Gating_St));

didn't compile was your use of <= instead of :=

Hmm, it worked form me. I have such a signal declared at the top level.
Maybe you had some pb with the hyphen lines wrapping (there's also a
commented out GatingState variable). BTW the reason of this is a work around
to display Gating_St for what I think is a Sonata bug with variable display.
Then the signal is triped by the compiler as the signal is not used
anywhere.



-- **************************************************************
-- CPLD instance
-- **************************************************************
--library metamor;
--use metamor.attributes.all;
LIBRARY ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

well done.



shr: process(SER_CLK)

can't afford a reset?
I don't know thanks to the size constrain. I have planned to rely on the
onboard uC to do startup reset for all the parts that need it.
When the design will be finished and will fit in, then I'll add all the
hardware resets needed if it fits in.



--------------------------------------------------------------------------
----
Parallel: process (Ck_1600KHz, CPLD_EXEC, Internal_100kHz_Clk,
PresetOn,
CalEnable, GatingEnable)

I thought Ck_1600KHz was your system clock. This should be

Parallel: process (CPLD_EXEC);

to match > if rising_edge(CPLD_EXEC) then
Ha ha, I think all that needs some clearing about the design so there's no
misunderstanding.

So :
The purpose of the CPLD is to manage the switches of a synchronous detector
and to provide different measurement methods (normal = averaging, averaged
time gated with output preset for fast acquisition, integrating). There are
also different calibration modes and some other minor things like
temperature monitoring chanel selection.

For the clocks there's one system reference clock (Ck_1600kHz) from which is
derived an external 100Khz IF (intermediate frequency) reference clock which
is resynchronized on board for the switches (no CPLD involved 'til this).
Then I have only one true system clock for the CPLD : Ck_1600kHz. All the
critical signals from the CPLD are then externally resynchronized to the
Ck_1600kHz.

Other clocking signals are :
* SER_CLK : generated from the uC. Used for shifting the registers of the
serial data bus link.
* CPLD_EXEC : generated from the uC. Just one pulse at the end of each
serial word to execute the pending command.
* Internal_100kHz_Clk : a reconstructed 100Khz IF clock, synchronous to the
external reference one (synchronized through GATE_TRIG) and used to count a
number (GateLength) of full IF cycles. It's done because it's easier to
bring an harmonic signal of the IF ref clock than the ref clock itself (the
If detector is ultra sensitive on its IF 100kHz ref frequency but is totally
insensitive to even harmonics of the IF 100kHz ref clock).

The onboard uC clock is asynchrone of Ck_1600kHz, so SER_DATA, SER_CLK and
CPLD_EXEC are signals of the uC clock domain, and Ck_1600kHz and GATE_TRIG
of the system clock domain. Also for the speed of the serial link I strongly
prefer the SER_DATA, SER_CLK and CPLD_EXEC signals not to be sampled by the
Ck_1600kHz clock.



-- *************************************
-- 100KHz clock predivider
if falling_edge(Ck_1600KHz) then
if Gating_St = WaitTrig_St then


No. you only get one edge per process.
Where does Ck_1600KHz : in std_logic come from?
Why can't I ? These FFs are an entirely different set of FFs from the ones
clocked one the Ck_1600kHz rising edge and also the CPLD_EXEC rising edge.
At least the code simulates fine and the synthetizer compiles fine and
infers FFs where I intended it to do and the fitter netlist does show what I
expected to see. So I'm a bit confused... Do I miss something ?
The reason, for me, to do this is that the Internal_100kHz_clk is used by
the gating_fsm which is clocked by the rising edge so it is perfectly stable
when used.

Also the reason I thought it was clever from you to put both clock domains
into one process was that the HoldMeas FF is shared and *updated* by both
sides. With the 2 processes of my first "design" I could only succeed to
handle this with 2 FFs, one from one side for signaling and another from the
other side for acknowledging. Works OK but it costs too much.


But the picture is now confusing (my lack of experience in this domain) or
perhaps is it the informations a gave above that where missing to you to ?

Thanks for clearing me.
Fred.



Here's the test bench, followed by a command file to copy in a "CPLDCMD.txt"
file.

***********************************************************************
-- Run this bench for 500 us
-- *******************************************
-- Serial commands generation for the CPLD
--
-- *******************************************
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
USE std.textio.all;

LIBRARY synopsys;
USE synopsys.std_logic_textio.all;

entity CPU_Cmd_block is
generic (
WIDTH : integer := 8;
duty : real := 50.0;
Tc : time := 100 ns
);
port (
rst : in std_logic := '0';
SendSeq : in std_logic;

SerOut : out std_logic;
SerClk : out std_logic;
CpldExec: out std_logic
);
END CPU_Cmd_block;

ARCHITECTURE behv of CPU_Cmd_block is
constant clockEdge : std_logic := '0';
constant BitsPerWord : integer := 8;
file CmdFile : text;

BEGIN
file_open(CmdFile,"CPLDCMD.txt",READ_MODE);


CPLDDriver : process
type fsm_state is (IDLE_St, SEND_St, END_St, READLINE_St);
variable Ser : std_logic;
variable fsm_stateM : fsm_state;
variable bit_count : integer;
variable Word : std_logic_vector(WIDTH-1 downto 0);
variable ByteToSend : std_logic_vector(WIDTH-1 downto 0);
variable line_in,
line_out : Line;

begin

case fsm_stateM is
when IDLE_St =>
CpldExec<= '0';
SerClk <= '0';
if SendSeq = '1' then
fsm_stateM := READLINE_St;
end if;

when READLINE_St =>
if not endfile(CmdFile) then
readline(CmdFile, line_in);
hread(line_in, ByteToSend);
Word := ByteToSend;
bit_count := 0;
if ByteToSend = X"FF" then -- suspend reading
fsm_StateM := IDLE_St;
elsif ByteToSend = X"FC" then
fsm_StateM := END_St;
else
fsm_StateM := SEND_St;
end if;
else
fsm_StateM := END_St;
end if;

when SEND_St =>
if bit_count >= BitsPerWord then
CpldExec<= '1', '0' after Tc*Duty/100.0;
fsm_stateM := READLINE_St;
else
SerOut <= Word(0);
Word := '0' & Word(WIDTH-1 downto 1);
bit_count := bit_count + 1;
SerClk<= '1' after Tc*Duty/100.0, '0' after Tc;
end if;

when END_St =>
file_close(CmdFile);
SerClk <= '0';
wait;
end case;

wait for Tc;
end process;
END behv;



--****************************************************************
--
-- TestBench definition
--****************************************************************
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY IF_CPLD_TB is
end IF_CPLD_TB;

ARCHITECTURE struct of IF_CPLD_TB is
use work.all;
signal SerClk : std_logic;
signal SerData : std_logic;
signal CpldExec: std_logic;
signal word : std_logic_vector(7 downto 0);
signal SendSeq : std_logic;
signal HF_clk : std_logic;
signal Gate_Trig : std_logic;

BEGIN

Ck1600k :process
begin
HF_clk <= '1', '0' after 312.5 ns;
wait for 625 ns;
end process;

GateTrig :process
begin
Gate_Trig <= '0' , '1' after 27 us, '0' after 29.8 us;
wait for 250 us; --150 us;
end process;

Send: process -- send bytes sequences to the CPLD
begin
SendSeq <= '0', '1' after (1 us), '0' after 3 us;
wait for 100 us;
loop
SendSeq <= '1', '0' after 200 ns;
wait for 15 us;
end loop;
wait;
end process;

WordGen : entity CPU_Cmd_block port map (SerClk => SerClk, SendSeq =>
SendSeq, SerOut => SerData, CpldExec=>CpldExec);

IF_CPLD : entity CPLD port map (SER_CLK => SerClk, SER_DATA=> SerData,
CPLD_EXEC=>CpldExec,
Ck_1600KHz => HF_clk,
GATE_TRIG => Gate_Trig);

END struct;



CPLDCMD.txt file. (starts right at the "60 Disable..." line. Do not include
the ***line)
***********************************************************************
60 Disable gating (normal mode)
10 Load Preset channel 0
07 load Temp channel 7
14 Enable OutputPreset
00 load Temp channel 0
10 Load Preset channel 0
60 Disable gating (normal mode)
50 Enable calibration
33 Load Gatecount = 38F
8f
91 Integrate
80 MeasRead
61 Enable gating
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
80 MeasRead
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
80
FC Close file
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
11
FF
12
FF
13
FF
10
FF
*************************************************
 
Fred Bartoli wrote:

Yep, metastability and all that stuff...
It's "that stuff" that's the bigger problem.
With a two flop synchronizer you're good for a lifetime.
With a one flop synchronizer you may be good for years.
With no synchronization, races will bite you every few minutes,
as data setup sweeps through the full range.


I agree and the mess of my first attempt came from that I first implemented
the hardware I new I needed, then tried to connect them... In fact I think I
was trying to use VHDL like ABEL, CUPL. Of course not the right way but I
must say that my domain of expertise is analog and I just did CPLDs on
occasions, to debug someone else production.
Using TTL sized entities is a fine way to
get started, but it is quite verbose.

I didn't expected you'll go to the trouble of running the code.
It takes far less time to run working a working testbench
than it does to visually parse non-working code.


In fact this is more a quick and dirty environment that evolved with
the needs of the CPLD portion to be tested than something with a rigorous
structure and doesn't respond to any industrial needs, i.e. I just have
inputs generation and no output tests for example (too much trouble at this
time).
All test benches start this way. You don't need to close
the loop until everything is working.

Anyway, I'll include it at the end, and also the commands input file for it.
I'll take a look at it at lunch time.

I have a working sim, and also a great concern about the size : I already
have the board designed, have to know wether the chossen package size is OK,
and also have to balance some functionnalities between the CPLD and a small
onboard uC that'll interpret some high level commands from the main CPU and
translate them to a list of low level commands for the CPLD.
Consider having the CPU do everything it is fast enough to handle.
Unless you've done this same board before, the odds are the
the it will have to be spun for one reason or another before your through.

I also feel a strong need to learn in any aspect of good VHDL coding as I
know that my design will probably just fit with the 64MC and I don't want to
restart coding again and again... This is may look slower at the beginning
but will make me win some time later. I'll also have other CPLDs to develop,
... and the main reason is probably that I've always done like this with
good fortune, so...
I agree with your outlook. The first outing with an HDL is not
going to save you any time over schematic entry, especially with
a small design. The big win is in simulating code at your desk
vs clipping little probes all over the board at the bench.



Hmm, it worked form me. I have such a signal declared at the top level.
Maybe you had some pb with the hyphen lines wrapping (there's also a
commented out GatingState variable).
Yes, I probably uncommment that.

BTW the reason of this is a work around
to display Gating_St for what I think is a Sonata bug with variable display.
The free version of Sonata doesn't display variables.

can't afford a reset?

I don't know thanks to the size constrain. I have planned to rely on the
onboard uC to do startup reset for all the parts that need it.
When the design will be finished and will fit in, then I'll add all the
hardware resets needed if it fits in.
It's fine to have the reset come from the cpu. Especially
if it's on the same clock.


The purpose of the CPLD is to manage the switches of a synchronous detector
and to provide different measurement methods (normal = averaging, averaged
time gated with output preset for fast acquisition, integrating). There are
also different calibration modes and some other minor things like
temperature monitoring chanel selection.
Cut and paste the text above as comments at the top of your architecture.

For the clocks there's one system reference clock (Ck_1600kHz) from which is
derived an external 100Khz IF (intermediate frequency) reference clock which
is resynchronized on board for the switches (no CPLD involved 'til this).
Then I have only one true system clock for the CPLD : Ck_1600kHz. All the
critical signals from the CPLD are then externally resynchronized to the
Ck_1600kHz.
In that case, consider using Ck_1600kHz as your only clock.
Generate different "clock enables" for each process running
at a slower speed. See:
http://groups.google.com/groups?q=clock+enable+vhdl+if+end

Other clocking signals are :
* SER_CLK : generated from the uC. Used for shifting the registers of the
serial data bus link.
* CPLD_EXEC : generated from the uC. Just one pulse at the end of each
serial word to execute the pending command.
* Internal_100kHz_Clk : a reconstructed 100Khz IF clock, synchronous to the
external reference one (synchronized through GATE_TRIG) and used to count a
number (GateLength) of full IF cycles. It's done because it's easier to
bring an harmonic signal of the IF ref clock than the ref clock itself (the
If detector is ultra sensitive on its IF 100kHz ref frequency but is totally
insensitive to even harmonics of the IF 100kHz ref clock).
These all become clock enables. Add text as comments.
The onboard uC clock is asynchrone of Ck_1600kHz, so SER_DATA, SER_CLK and
CPLD_EXEC are signals of the uC clock domain, and Ck_1600kHz and GATE_TRIG
of the system clock domain. Also for the speed of the serial link I strongly
prefer the SER_DATA, SER_CLK and CPLD_EXEC signals not to be sampled by the
Ck_1600kHz clock.
OK, if uC clock is faster than 1600kHz, use that as your main clock, and
make a synchronous pulse at the 1600kHz rate.

-- 100KHz clock predivider
if falling_edge(Ck_1600KHz) then
if Gating_St = WaitTrig_St then


No. you only get one edge per process.
Where does Ck_1600KHz : in std_logic come from?

Why can't I ?
Now that I look at your code, I don't even see that
bit any more.
I like to make all processes look like this:


-----------------------------------------
process(reset,clock) is
-- declare local variables, functions, procedures here
begin
if (reset='1') then
-- asynch reset assignments
elsif rising_edge(clock) then
if(enable='1') then
-- synchronous reset statements
-- sequential statement ( <=, :=, if, case, for etc.)
-- sequential statement ( <=, :=, if, case, for etc.)
-- sequential statement ( <=, :=, if, case, for etc.)
-- sequential statement ( <=, :=, if, case, for etc.)
end if;
-- must make assignments ( <= ) to at least one
-- entity port (directly or indirectly)
-- to avoid a null synthesis.
end if;
end process;
-----------------------------------------------

I'll have a look at the sim. and get back.

-- Mike Treseler
 
Mike Treseler wrote:

I'll have a look at the sim. and get back.
Fred, your sim ran ok for me.
Like you said, not everything is hooked up
and synced up, but it does run.

Here's how modelsim sees your processes:
http://home.comcast.net/~mike_treseler/detector.pdf

I expect that this whole controller could be
run from the cpu constant clock.
I assume that the cpu clk is several
times faster than your ser_clk bursts.

ser_clk and ser_data would become inputs
to a synchronous process that shifts out
a ser_data bit every time ser_clk is high
and the previous ser_clk was low.

Here's one way to do this:
http://groups.google.com/groups?q=vhdl+ck_rising

-- Mike Treseler
 
"Mike Treseler" <tres@fluke.com> a écrit dans le message news:
402432D7.5060503@fluke.com...
Mike Treseler wrote:

I'll have a look at the sim. and get back.

Fred, your sim ran ok for me.
Like you said, not everything is hooked up
and synced up, but it does run.

Here's how modelsim sees your processes:
http://home.comcast.net/~mike_treseler/detector.pdf

I expect that this whole controller could be
run from the cpu constant clock.
I assume that the cpu clk is several
times faster than your ser_clk bursts.

ser_clk and ser_data would become inputs
to a synchronous process that shifts out
a ser_data bit every time ser_clk is high
and the previous ser_clk was low.

Here's one way to do this:
http://groups.google.com/groups?q=vhdl+ck_rising

-- Mike Treseler
I've now implemented almost all of the logic, the sync pb apart, and have
come short of CPLD ressources, but I've coded it... crudely first to check
some ideas. I have to polish all this.

Also I now have a better understanding of the synchronisation pb and some
solutions and have to think to all this.

However that's now time for a week off :)

I'll get back when more advanced/almost finished, but I'm somewhat afraid
about the CPLD size.

Thanks,
Fred.
 

Welcome to EDABoard.com

Sponsor

Back
Top