| -- |
| -- Copyright (C) 2015-2016, 2019 secunet Security Networks AG |
| -- Copyright (C) 2017 Nico Huber <nico.h@gmx.de> |
| -- |
| -- This program is free software; you can redistribute it and/or modify |
| -- it under the terms of the GNU General Public License as published by |
| -- the Free Software Foundation; either version 2 of the License, or |
| -- (at your option) any later version. |
| -- |
| -- This program is distributed in the hope that it will be useful, |
| -- but WITHOUT ANY WARRANTY; without even the implied warranty of |
| -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| -- GNU General Public License for more details. |
| -- |
| |
| with Ada.Unchecked_Conversion; |
| |
| with HW.Debug; |
| with GNAT.Source_Info; |
| |
| with HW.Time; |
| with HW.GFX.DP_Defs; |
| |
| package body HW.GFX.DP_Training is |
| |
| pragma Warnings (GNATprove, Off, "unused initial value of ""Port""*", |
| Reason => "Needed for a common interface"); |
| function Training_Set |
| (Port : T; |
| Train_Set : DP_Info.Train_Set) |
| return Word8 |
| is |
| use type DP_Info.DP_Voltage_Swing; |
| use type DP_Info.DP_Pre_Emph; |
| use type Word8; |
| Value : Word8; |
| begin |
| case Train_Set.Voltage_Swing is |
| when DP_Info.VS_Level_0 => Value := 16#00#; |
| when DP_Info.VS_Level_1 => Value := 16#01#; |
| when DP_Info.VS_Level_2 => Value := 16#02#; |
| when DP_Info.VS_Level_3 => Value := 16#03#; |
| end case; |
| if Train_Set.Voltage_Swing = Max_V_Swing (Port) then |
| Value := Value or 16#04#; |
| end if; |
| |
| case Train_Set.Pre_Emph is |
| when DP_Info.Emph_Level_0 => Value := Value or 16#00#; |
| when DP_Info.Emph_Level_1 => Value := Value or 16#08#; |
| when DP_Info.Emph_Level_2 => Value := Value or 16#10#; |
| when DP_Info.Emph_Level_3 => Value := Value or 16#18#; |
| end case; |
| if Train_Set.Pre_Emph = Max_Pre_Emph (Port, Train_Set) then |
| Value := Value or 16#20#; |
| end if; |
| |
| return Value; |
| end Training_Set; |
| pragma Warnings (GNATprove, On, "unused initial value of ""Port""*"); |
| |
| ---------------------------------------------------------------------------- |
| |
| function Lane_Count (Link : DP_Link) return Positive |
| with |
| Post => Lane_Count'Result <= 4 |
| is |
| begin |
| return Positive (Lane_Count_As_Integer (Link.Lane_Count)); |
| end Lane_Count; |
| |
| procedure Sink_Init |
| (Port : in Aux_T; |
| Link : in DP_Link; |
| Success : out Boolean) |
| is |
| use type Word8; |
| function Link_Rate_As_Word8 is new Ada.Unchecked_Conversion |
| (Source => DP_Bandwidth, Target => Word8); |
| Data : DP_Defs.Aux_Payload; |
| begin |
| pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); |
| |
| Data := |
| (0 => Link_Rate_As_Word8 (Link.Bandwidth), |
| 1 => Word8 (Lane_Count (Link)), |
| others => 0); -- Don't care |
| |
| if Link.Enhanced_Framing then |
| Data (1) := Data (1) or 16#80#; |
| end if; |
| |
| Aux_Ch.Aux_Write |
| (Port => Port, |
| Address => 16#00100#, -- LINK_BW_SET, LANE_COUNT_SET |
| Length => 2, |
| Data => Data, |
| Success => Success); |
| Success := Success or Link.Opportunistic_Training; |
| |
| if Success then |
| Data (0) := 16#00#; -- no downspread |
| Data (1) := 16#01#; -- ANSI8B10B coding |
| |
| Aux_Ch.Aux_Write |
| (Port => Port, |
| Address => 16#00107#, -- DOWNSPREAD_CTRL, |
| Length => 2, -- MAIN_LINK_CHANNEL_CODING_SET |
| Data => Data, |
| Success => Success); |
| Success := Success or Link.Opportunistic_Training; |
| end if; |
| end Sink_Init; |
| |
| procedure Sink_Set_Training_Pattern |
| (Port : in T; |
| Link : in DP_Link; |
| Pattern : in DP_Info.Training_Pattern; |
| Train_Set : in DP_Info.Train_Set; |
| Success : out Boolean) |
| is |
| use type DP_Info.Training_Pattern; |
| |
| type TP_Array is array (DP_Info.Training_Pattern) of Word8; |
| TP : constant TP_Array := TP_Array' |
| (DP_Info.TP_1 => 16#21#, DP_Info.TP_2 => 16#22#, DP_Info.TP_3 => 16#23#, |
| DP_Info.TP_Idle => 16#00#, DP_Info.TP_None => 16#00#); |
| T_Set : constant Word8 := Training_Set (Port, Train_Set); |
| |
| Data : DP_Defs.Aux_Payload; |
| Length : Positive := 1; |
| begin |
| pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); |
| |
| Data := (TP (Pattern), others => 0); |
| if Pattern < DP_Info.TP_Idle then |
| Length := Length + Lane_Count (Link); |
| Data (1 .. Lane_Count (Link)) := (others => T_Set); |
| end if; |
| |
| Aux_Ch.Aux_Write |
| (Port => To_Aux (Port), |
| Address => 16#00102#, -- TRAINING_PATTERN_SET |
| Length => Length, |
| Data => Data, |
| Success => Success); |
| end Sink_Set_Training_Pattern; |
| |
| procedure Sink_Set_Signal_Levels |
| (Port : in T; |
| DP : in Aux_T; |
| Link : in DP_Link; |
| Train_Set : in DP_Info.Train_Set; |
| Success : out Boolean) |
| is |
| Data : DP_Defs.Aux_Payload; |
| T_Set : constant Word8 := Training_Set (Port, Train_Set); |
| begin |
| pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); |
| |
| Data := (others => 0); -- Initialize |
| Data (0 .. Lane_Count (Link) - 1) := (others => T_Set); |
| |
| Aux_Ch.Aux_Write |
| (Port => DP, |
| Address => 16#00103#, -- TRAINING_LANEx_SET |
| Length => Lane_Count (Link), |
| Data => Data, |
| Success => Success); |
| end Sink_Set_Signal_Levels; |
| |
| pragma Warnings (GNATprove, Off, "unused initial value of ""Port""*", |
| Reason => "Needed for a common interface"); |
| procedure Sink_Adjust_Training |
| (Port : in T; |
| DP : in Aux_T; |
| Link : in DP_Link; |
| Train_Set : in out DP_Info.Train_Set; |
| CR_Done : in out Boolean; |
| EQ_Done : out Boolean; |
| Success : out Boolean) |
| is |
| use type DP_Info.DP_Voltage_Swing; |
| use type DP_Info.DP_Pre_Emph; |
| |
| Status : DP_Info.Link_Status; |
| CR_Was_Done : constant Boolean := CR_Done; |
| |
| pragma Warnings |
| (GNATprove, Off, "subprogram ""Dump_Link_Status"" has no effect*", |
| Reason => "It's only used for debugging"); |
| procedure Dump_Link_Status |
| is |
| begin |
| Debug.New_Line; |
| Debug.Put_Line ("Link Status:"); |
| |
| for Lane in DP_Info.Lane_Index range 0 |
| .. DP_Info.Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1) |
| loop |
| Debug.Put (" Lane"); |
| Debug.Put_Int8 (Int8 (Lane)); |
| Debug.Put_Line (":"); |
| |
| Debug.Put_Line (" CR_Done : " & |
| (if Status.Lanes (Lane).CR_Done then "1" else "0")); |
| Debug.Put_Line (" Channel_EQ_Done: " & |
| (if Status.Lanes (Lane).Channel_EQ_Done then "1" else "0")); |
| Debug.Put_Line (" Symbol_Locked : " & |
| (if Status.Lanes (Lane).Symbol_Locked then "1" else "0")); |
| end loop; |
| |
| Debug.Put_Line (" Interlane_Align_Done: " & |
| (if Status.Interlane_Align_Done then "1" else "0")); |
| |
| for Lane in DP_Info.Lane_Index range 0 |
| .. DP_Info.Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1) |
| loop |
| Debug.Put (" Adjust"); |
| Debug.Put_Int8 (Int8 (Lane)); |
| Debug.Put_Line (":"); |
| |
| Debug.Put (" Voltage_Swing: "); |
| Debug.Put_Int8 (Int8 (DP_Info.DP_Voltage_Swing'Pos |
| (Status.Adjust_Requests (Lane).Voltage_Swing))); |
| Debug.New_Line; |
| Debug.Put (" Pre_Emph : "); |
| Debug.Put_Int8 (Int8 (DP_Info.DP_Pre_Emph'Pos |
| (Status.Adjust_Requests (Lane).Pre_Emph))); |
| Debug.New_Line; |
| end loop; |
| |
| Debug.New_Line; |
| end Dump_Link_Status; |
| begin |
| pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); |
| |
| DP_Info.Read_Link_Status |
| (Port => DP, |
| Status => Status, |
| Success => Success); |
| |
| pragma Debug (Success, Dump_Link_Status); |
| |
| CR_Done := Success and then DP_Info.All_CR_Done (Status, Link); |
| EQ_Done := Success and then DP_Info.All_EQ_Done (Status, Link); |
| Success := Success and then (CR_Done or not CR_Was_Done); |
| |
| -- Voltage swing may be updated during channel equalization too. |
| if Success and not EQ_Done then |
| Train_Set.Voltage_Swing := |
| DP_Info.Max_Requested_VS (Status, Link); |
| if Train_Set.Voltage_Swing > Max_V_Swing (Port) |
| then |
| Train_Set.Voltage_Swing := Max_V_Swing (Port); |
| end if; |
| end if; |
| |
| -- According to DP spec, only change preemphasis during channel |
| -- equalization. What to do if sink requests it during clock recovery? |
| -- Linux always accepts new values from the sink, we too, now: There |
| -- are sinks in the wild that need this. |
| if Success and not EQ_Done then |
| Train_Set.Pre_Emph := |
| DP_Info.Max_Requested_Emph (Status, Link); |
| if Train_Set.Pre_Emph > Max_Pre_Emph (Port, Train_Set) |
| then |
| Train_Set.Pre_Emph := Max_Pre_Emph (Port, Train_Set); |
| end if; |
| end if; |
| end Sink_Adjust_Training; |
| pragma Warnings (GNATprove, On, "unused initial value of ""Port""*"); |
| |
| ---------------------------------------------------------------------------- |
| |
| procedure Train_DP |
| (Port : in T; |
| Link : in DP_Link; |
| Success : out Boolean) |
| is |
| use type DP_Info.DP_Voltage_Swing; |
| use type DP_Info.DP_Pre_Emph; |
| use type Word8; |
| |
| DP : constant Aux_T := To_Aux (Port); |
| |
| Retries : Natural; |
| Max_Retry : constant := 4; |
| CR_Done, EQ_Done : Boolean := False; |
| |
| EQ_Pattern : constant DP_Info.Training_Pattern := |
| (if TPS3_Supported and Link.Receiver_Caps.TPS3_Supported then |
| DP_Info.TP_3 |
| else |
| DP_Info.TP_2); |
| |
| Train_Set, Last_Train_Set : DP_Info.Train_Set; |
| |
| function CR_Delay return Natural is |
| Result : Natural := 100; -- DP spec: 100us |
| begin |
| if Link.Bandwidth = DP_Bandwidth_5_4 and |
| Link.Receiver_Caps.Aux_RD_Interval /= 0 |
| then |
| Result := Natural (Link.Receiver_Caps.Aux_RD_Interval) * 4_000; |
| end if; |
| return Result; |
| end CR_Delay; |
| |
| function EQ_Delay return Natural is |
| Result : Natural := 400; -- DP spec: 400us |
| begin |
| if Link.Bandwidth = DP_Bandwidth_5_4 and |
| Link.Receiver_Caps.Aux_RD_Interval /= 0 |
| then |
| Result := Natural (Link.Receiver_Caps.Aux_RD_Interval) * 4_000; |
| end if; |
| return Result; |
| end EQ_Delay; |
| begin |
| pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); |
| |
| Train_Set.Voltage_Swing := DP_Info.DP_Voltage_Swing'First; |
| Train_Set.Pre_Emph := DP_Info.DP_Pre_Emph'First; |
| |
| Set_Pattern (Port, Link, DP_Info.TP_1); |
| Set_Signal_Levels (Port, Link, Train_Set); |
| |
| pragma Warnings |
| (GNATprove, Off, """Success"" modified by call, but value overwritten*", |
| Reason => "Read first, then overwritten, looks like a false positive"); |
| Sink_Init (DP, Link, Success); |
| pragma Warnings |
| (GNATprove, On, """Success"" modified by call, but value overwritten*"); |
| if Success then |
| Sink_Set_Training_Pattern |
| (Port, Link, DP_Info.TP_1, Train_Set, Success); |
| end if; |
| |
| if Success then |
| Retries := 0; |
| for Tries in 1 .. 32 loop |
| pragma Loop_Invariant (Retries <= Max_Retry); |
| |
| Time.U_Delay (CR_Delay); |
| |
| Last_Train_Set := Train_Set; |
| Sink_Adjust_Training |
| (Port, DP, Link, Train_Set, CR_Done, EQ_Done, Success); |
| exit when CR_Done or not Success; |
| |
| if Train_Set.Voltage_Swing = Last_Train_Set.Voltage_Swing then |
| exit when Retries = Max_Retry; |
| Retries := Retries + 1; |
| else |
| exit when Last_Train_Set.Voltage_Swing = Max_V_Swing (Port); |
| Retries := 0; |
| end if; |
| |
| Set_Signal_Levels (Port, Link, Train_Set); |
| Sink_Set_Signal_Levels (Port, DP, Link, Train_Set, Success); |
| exit when not Success; |
| end loop; |
| end if; |
| |
| Success := Success and CR_Done; |
| |
| if Success then |
| Set_Pattern (Port, Link, EQ_Pattern); |
| Sink_Set_Training_Pattern (Port, Link, EQ_Pattern, Train_Set, Success); |
| end if; |
| |
| if Success then |
| for Tries in 1 .. 6 loop |
| Time.U_Delay (EQ_Delay); |
| |
| Sink_Adjust_Training |
| (Port, DP, Link, Train_Set, CR_Done, EQ_Done, Success); |
| exit when EQ_Done or not Success; |
| |
| Set_Signal_Levels (Port, Link, Train_Set); |
| Sink_Set_Signal_Levels (Port, DP, Link, Train_Set, Success); |
| exit when not Success; |
| end loop; |
| end if; |
| |
| -- Set_Pattern (TP_None) includes sending the Idle Pattern, |
| -- so tell sink first. |
| Sink_Set_Training_Pattern |
| (Port, Link, DP_Info.TP_None, Train_Set, Success); |
| Set_Pattern (Port, Link, DP_Info.TP_None); |
| |
| Success := Success and then EQ_Done; |
| if not Success then |
| Off (Port); |
| end if; |
| end Train_DP; |
| |
| end HW.GFX.DP_Training; |