OK, what’s this?
This page is the example part of another post, which explains the meaning of set_input_delay and set_output_delay in SDC timing constraints.
TimeQuest (Quartus’ timing analyzer) performs a four-corner check (max/min temperature, max/min voltage) and picks the worst slack. In the examples below, the worst case of these four corners is shown. It’s not exactly clear why a certain delay model becomes the worst case all the times.
Another post of mine discusses the generation of timing reports as shown below.
As mentioned on the other post, the relevant timing constraints were:
create_clock -name theclk -period 20 [get_ports test_clk]
set_output_delay -clock theclk -max 8 [get_ports test_out]
set_output_delay -clock theclk -min -3 [get_ports test_out]
set_input_delay -clock theclk -max 4 [get_ports test_in]
set_input_delay -clock theclk -min 2 [get_ports test_in]
set_input_delay -max timing analysis (setup)
Delay Model:
Slow 1100mV 0C Model
+------------------------------------------------------------------------------------------------------+
; Summary of Paths ;
+--------+-----------+-----------+--------------+-------------+--------------+------------+------------+
; Slack ; From Node ; To Node ; Launch Clock ; Latch Clock ; Relationship ; Clock Skew ; Data Delay ;
+--------+-----------+-----------+--------------+-------------+--------------+------------+------------+
; 12.341 ; test_in ; test_samp ; theclk ; theclk ; 20.000 ; 3.940 ; 7.499 ;
+--------+-----------+-----------+--------------+-------------+--------------+------------+------------+
Path #1: Setup slack is 12.341
===============================================================================
+--------------------------------+
; Path Summary ;
+--------------------+-----------+
; Property ; Value ;
+--------------------+-----------+
; From Node ; test_in ;
; To Node ; test_samp ;
; Launch Clock ; theclk ;
; Latch Clock ; theclk ;
; Data Arrival Time ; 11.499 ;
; Data Required Time ; 23.840 ;
; Slack ; 12.341 ;
+--------------------+-----------+
+---------------------------------------------------------------------------------------+
; Statistics ;
+---------------------------+--------+-------+-------------+------------+-------+-------+
; Property ; Value ; Count ; Total Delay ; % of Total ; Min ; Max ;
+---------------------------+--------+-------+-------------+------------+-------+-------+
; Setup Relationship ; 20.000 ; ; ; ; ; ;
; Clock Skew ; 3.940 ; ; ; ; ; ;
; Data Delay ; 7.499 ; ; ; ; ; ;
; Number of Logic Levels ; ; 1 ; ; ; ; ;
; Physical Delays ; ; ; ; ; ; ;
; Arrival Path ; ; ; ; ; ; ;
; Clock ; ; ; ; ; ; ;
; Clock Network (Lumped) ; ; 1 ; 0.000 ; ; 0.000 ; 0.000 ;
; Data ; ; ; ; ; ; ;
; IC ; ; 2 ; 2.447 ; 33 ; 0.000 ; 2.447 ;
; Cell ; ; 2 ; 5.052 ; 67 ; 0.652 ; 4.400 ;
; Required Path ; ; ; ; ; ; ;
; Clock ; ; ; ; ; ; ;
; Clock Network (Lumped) ; ; 1 ; 3.940 ; 100 ; 3.940 ; 3.940 ;
+---------------------------+--------+-------+-------------+------------+-------+-------+
Note: Negative delays are omitted from totals when calculating percentages
+-----------------------------------------------------------------------------------+
; Data Arrival Path ;
+----------+---------+----+------+--------+-------------------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+----------+---------+----+------+--------+-------------------+---------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 0.000 ; 0.000 ; ; ; ; ; clock path ;
; 0.000 ; 0.000 ; R ; ; ; ; clock network delay ;
; 4.000 ; 4.000 ; F ; iExt ; 1 ; PIN_AP17 ; test_in ;
; 11.499 ; 7.499 ; ; ; ; ; data path ;
; 4.000 ; 0.000 ; FF ; IC ; 1 ; IOIBUF_X48_Y0_N58 ; test_in~input|i ;
; 8.400 ; 4.400 ; FF ; CELL ; 1 ; IOIBUF_X48_Y0_N58 ; test_in~input|o ;
; 10.847 ; 2.447 ; FF ; IC ; 1 ; FF_X48_Y2_N40 ; test_samp|asdata ;
; 11.499 ; 0.652 ; FF ; CELL ; 1 ; FF_X48_Y2_N40 ; test_samp ;
+----------+---------+----+------+--------+-------------------+---------------------+
+-------------------------------------------------------------------------------+
; Data Required Path ;
+----------+---------+----+------+--------+---------------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+----------+---------+----+------+--------+---------------+---------------------+
; 20.000 ; 20.000 ; ; ; ; ; latch edge time ;
; 23.940 ; 3.940 ; ; ; ; ; clock path ;
; 23.940 ; 3.940 ; R ; ; ; ; clock network delay ;
; 23.840 ; -0.100 ; ; ; ; ; clock uncertainty ;
; 23.840 ; 0.000 ; ; uTsu ; 1 ; FF_X48_Y2_N40 ; test_samp ;
+----------+---------+----+------+--------+---------------+---------------------+
This analysis starts in “Data Arrival Path” with setting the input port (test_in) at 4 ns as specified in the max input delay constraint, and continues that data path. Together with the FPGA’s own data path delay (7.499 ns), the total data path delay stands at 11.499 ns.
The clock path is the calculated in “Data Required Path”, starting from the following clock at 20 ns. The clock travels from the input pin to the flip-flop (with no clock network delay compensation, since no PLL is involved), taking into account the calculated jitter. All in all, the clock path ends at 23.840 ns, which is 12.341 ns after the data arrived to the flip-flop, which is this constraint’s slack.
It’s simple to see from this analysis that the max input delay is the clock-to-output ( + board delay), as it’s the starting time of the data path.
set_input_delay -min timing analysis (hold)
Delay Model:
Slow 1100mV 85C Model
+-----------------------------------------------------------------------------------------------------+
; Summary of Paths ;
+-------+-----------+-----------+--------------+-------------+--------------+------------+------------+
; Slack ; From Node ; To Node ; Launch Clock ; Latch Clock ; Relationship ; Clock Skew ; Data Delay ;
+-------+-----------+-----------+--------------+-------------+--------------+------------+------------+
; 0.770 ; test_in ; test_samp ; theclk ; theclk ; 0.000 ; 4.287 ; 3.057 ;
+-------+-----------+-----------+--------------+-------------+--------------+------------+------------+
Path #1: Hold slack is 0.770
===============================================================================
+--------------------------------+
; Path Summary ;
+--------------------+-----------+
; Property ; Value ;
+--------------------+-----------+
; From Node ; test_in ;
; To Node ; test_samp ;
; Launch Clock ; theclk ;
; Latch Clock ; theclk ;
; Data Arrival Time ; 5.057 ;
; Data Required Time ; 4.287 ;
; Slack ; 0.770 ;
+--------------------+-----------+
+--------------------------------------------------------------------------------------+
; Statistics ;
+---------------------------+-------+-------+-------------+------------+-------+-------+
; Property ; Value ; Count ; Total Delay ; % of Total ; Min ; Max ;
+---------------------------+-------+-------+-------------+------------+-------+-------+
; Hold Relationship ; 0.000 ; ; ; ; ; ;
; Clock Skew ; 4.287 ; ; ; ; ; ;
; Data Delay ; 3.057 ; ; ; ; ; ;
; Number of Logic Levels ; ; 1 ; ; ; ; ;
; Physical Delays ; ; ; ; ; ; ;
; Arrival Path ; ; ; ; ; ; ;
; Clock ; ; ; ; ; ; ;
; Clock Network (Lumped) ; ; 1 ; 0.000 ; ; 0.000 ; 0.000 ;
; Data ; ; ; ; ; ; ;
; IC ; ; 2 ; 2.028 ; 66 ; 0.000 ; 2.028 ;
; Cell ; ; 2 ; 1.029 ; 34 ; 0.290 ; 0.739 ;
; Required Path ; ; ; ; ; ; ;
; Clock ; ; ; ; ; ; ;
; Clock Network (Lumped) ; ; 1 ; 4.287 ; 100 ; 4.287 ; 4.287 ;
+---------------------------+-------+-------+-------------+------------+-------+-------+
Note: Negative delays are omitted from totals when calculating percentages
+----------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+---------+----+------+--------+-------------------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+-------------------+---------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 0.000 ; 0.000 ; ; ; ; ; clock path ;
; 0.000 ; 0.000 ; R ; ; ; ; clock network delay ;
; 2.000 ; 2.000 ; R ; iExt ; 1 ; PIN_AP17 ; test_in ;
; 5.057 ; 3.057 ; ; ; ; ; data path ;
; 2.000 ; 0.000 ; RR ; IC ; 1 ; IOIBUF_X48_Y0_N58 ; test_in~input|i ;
; 2.739 ; 0.739 ; RR ; CELL ; 1 ; IOIBUF_X48_Y0_N58 ; test_in~input|o ;
; 4.767 ; 2.028 ; RR ; IC ; 1 ; FF_X48_Y2_N40 ; test_samp|asdata ;
; 5.057 ; 0.290 ; RR ; CELL ; 1 ; FF_X48_Y2_N40 ; test_samp ;
+---------+---------+----+------+--------+-------------------+---------------------+
+------------------------------------------------------------------------------+
; Data Required Path ;
+---------+---------+----+------+--------+---------------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+---------------+---------------------+
; 0.000 ; 0.000 ; ; ; ; ; latch edge time ;
; 4.287 ; 4.287 ; ; ; ; ; clock path ;
; 4.287 ; 4.287 ; R ; ; ; ; clock network delay ;
; 4.287 ; 0.000 ; ; ; ; ; clock uncertainty ;
; 4.287 ; 0.000 ; ; uTh ; 1 ; FF_X48_Y2_N40 ; test_samp ;
+---------+---------+----+------+--------+---------------+---------------------+
This analysis starts in “Data Arrival Path” with setting the input port (test_in) at 2 ns as specified in the min input delay constraint, and continues that data path. Together with the FPGA’s own data path delay (3.057 ns), the total data path delay stands at 5.057 ns.
The clock path is the calculated in “Data Required Path”, starting from the same clock edge at 0 ns. After all, this is a hold calculation, so the question is whether the mat wasn’t swept under the feet of the sampling flip-flop before it managed to sample it.
The clock travels from the input pin to the flip-flop (with no clock network delay compensation, since no PLL is involved), taking into account the calculated jitter. All in all, the clock path ends at 4.287 ns, which is 0.770 ns earlier than the data switching, which is also the slack.
It’s simple to see from this analysis that the min input delay is the minimal clock-to-output, as it’s the starting time of the data path.
set_output_delay -max timing analysis (setup)
Delay Model:
Slow 1100mV 85C Model
+--------------------------------------------------------------------------------------------------------+
; Summary of Paths ;
+-------+---------------+----------+--------------+-------------+--------------+------------+------------+
; Slack ; From Node ; To Node ; Launch Clock ; Latch Clock ; Relationship ; Clock Skew ; Data Delay ;
+-------+---------------+----------+--------------+-------------+--------------+------------+------------+
; 2.651 ; test_out~reg0 ; test_out ; theclk ; theclk ; 20.000 ; -5.320 ; 3.929 ;
+-------+---------------+----------+--------------+-------------+--------------+------------+------------+
Path #1: Setup slack is 2.651
===============================================================================
+------------------------------------+
; Path Summary ;
+--------------------+---------------+
; Property ; Value ;
+--------------------+---------------+
; From Node ; test_out~reg0 ;
; To Node ; test_out ;
; Launch Clock ; theclk ;
; Latch Clock ; theclk ;
; Data Arrival Time ; 9.249 ;
; Data Required Time ; 11.900 ;
; Slack ; 2.651 ;
+--------------------+---------------+
+---------------------------------------------------------------------------------------+
; Statistics ;
+---------------------------+--------+-------+-------------+------------+-------+-------+
; Property ; Value ; Count ; Total Delay ; % of Total ; Min ; Max ;
+---------------------------+--------+-------+-------------+------------+-------+-------+
; Setup Relationship ; 20.000 ; ; ; ; ; ;
; Clock Skew ; -5.320 ; ; ; ; ; ;
; Data Delay ; 3.929 ; ; ; ; ; ;
; Number of Logic Levels ; ; 0 ; ; ; ; ;
; Physical Delays ; ; ; ; ; ; ;
; Arrival Path ; ; ; ; ; ; ;
; Clock ; ; ; ; ; ; ;
; Clock Network (Lumped) ; ; 1 ; 5.320 ; 100 ; 5.320 ; 5.320 ;
; Data ; ; ; ; ; ; ;
; IC ; ; 1 ; 0.000 ; 0 ; 0.000 ; 0.000 ;
; Cell ; ; 3 ; 3.929 ; 100 ; 0.000 ; 2.150 ;
; uTco ; ; 1 ; 0.000 ; 0 ; 0.000 ; 0.000 ;
; Required Path ; ; ; ; ; ; ;
; Clock ; ; ; ; ; ; ;
; Clock Network (Lumped) ; ; 1 ; 0.000 ; ; 0.000 ; 0.000 ;
+---------------------------+--------+-------+-------------+------------+-------+-------+
Note: Negative delays are omitted from totals when calculating percentages
+---------------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+---------+----+------+--------+------------------------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+------------------------+---------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 5.320 ; 5.320 ; ; ; ; ; clock path ;
; 5.320 ; 5.320 ; R ; ; ; ; clock network delay ;
; 9.249 ; 3.929 ; ; ; ; ; data path ;
; 5.320 ; 0.000 ; ; uTco ; 1 ; DDIOOUTCELL_X48_Y0_N50 ; test_out~reg0 ;
; 7.099 ; 1.779 ; FF ; CELL ; 1 ; DDIOOUTCELL_X48_Y0_N50 ; test_out~reg0|q ;
; 7.099 ; 0.000 ; FF ; IC ; 1 ; IOOBUF_X48_Y0_N42 ; test_out~output|i ;
; 9.249 ; 2.150 ; FF ; CELL ; 1 ; IOOBUF_X48_Y0_N42 ; test_out~output|o ;
; 9.249 ; 0.000 ; FF ; CELL ; 0 ; PIN_AN17 ; test_out ;
+---------+---------+----+------+--------+------------------------+---------------------+
+--------------------------------------------------------------------------+
; Data Required Path ;
+----------+---------+----+------+--------+----------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+----------+---------+----+------+--------+----------+---------------------+
; 20.000 ; 20.000 ; ; ; ; ; latch edge time ;
; 20.000 ; 0.000 ; ; ; ; ; clock path ;
; 20.000 ; 0.000 ; R ; ; ; ; clock network delay ;
; 19.900 ; -0.100 ; ; ; ; ; clock uncertainty ;
; 11.900 ; -8.000 ; F ; oExt ; 0 ; PIN_AN17 ; test_out ;
+----------+---------+----+------+--------+----------+---------------------+
Since the purpose of this analysis is to measure the output delay, it starts off in “Data Arrival Path” with the clock edge, adds the clock network delay to the flip-flop, and then goes along the data path until the physical output is stable, calculated at 9.249 ns.
This is compared with the time of the following clock at 20 ns, minus the output delay. Minus the possible jitter (0.1 ns in the case above). Data arrived at 9.249 ns, the moment that counts is at 11.9 ns, so there’s a 2.651 ns slack.
This demonstrates why set_output_delay -max is the setup time of the receiver: The output delay is reduced from the following clock’s time position, and that’s the goal to meet. That’s exactly the definition of setup time: How long before the following clock the data must be stable.
set_output_delay -min timing analysis (hold)
Delay Model:
Fast 1100mV 0C Model
+--------------------------------------------------------------------------------------------------------+
; Summary of Paths ;
+-------+---------------+----------+--------------+-------------+--------------+------------+------------+
; Slack ; From Node ; To Node ; Launch Clock ; Latch Clock ; Relationship ; Clock Skew ; Data Delay ;
+-------+---------------+----------+--------------+-------------+--------------+------------+------------+
; 1.275 ; test_out~reg0 ; test_out ; theclk ; theclk ; 0.000 ; -2.255 ; 2.020 ;
+-------+---------------+----------+--------------+-------------+--------------+------------+------------+
Path #1: Hold slack is 1.275
===============================================================================
+------------------------------------+
; Path Summary ;
+--------------------+---------------+
; Property ; Value ;
+--------------------+---------------+
; From Node ; test_out~reg0 ;
; To Node ; test_out ;
; Launch Clock ; theclk ;
; Latch Clock ; theclk ;
; Data Arrival Time ; 4.275 ;
; Data Required Time ; 3.000 ;
; Slack ; 1.275 ;
+--------------------+---------------+
+---------------------------------------------------------------------------------------+
; Statistics ;
+---------------------------+--------+-------+-------------+------------+-------+-------+
; Property ; Value ; Count ; Total Delay ; % of Total ; Min ; Max ;
+---------------------------+--------+-------+-------------+------------+-------+-------+
; Hold Relationship ; 0.000 ; ; ; ; ; ;
; Clock Skew ; -2.255 ; ; ; ; ; ;
; Data Delay ; 2.020 ; ; ; ; ; ;
; Number of Logic Levels ; ; 0 ; ; ; ; ;
; Physical Delays ; ; ; ; ; ; ;
; Arrival Path ; ; ; ; ; ; ;
; Clock ; ; ; ; ; ; ;
; Clock Network (Lumped) ; ; 1 ; 2.255 ; 100 ; 2.255 ; 2.255 ;
; Data ; ; ; ; ; ; ;
; IC ; ; 1 ; 0.000 ; 0 ; 0.000 ; 0.000 ;
; Cell ; ; 3 ; 2.020 ; 100 ; 0.000 ; 1.296 ;
; uTco ; ; 1 ; 0.000 ; 0 ; 0.000 ; 0.000 ;
; Required Path ; ; ; ; ; ; ;
; Clock ; ; ; ; ; ; ;
; Clock Network (Lumped) ; ; 1 ; 0.000 ; ; 0.000 ; 0.000 ;
+---------------------------+--------+-------+-------------+------------+-------+-------+
Note: Negative delays are omitted from totals when calculating percentages
+---------------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+---------+----+------+--------+------------------------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+------------------------+---------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 2.255 ; 2.255 ; ; ; ; ; clock path ;
; 2.255 ; 2.255 ; R ; ; ; ; clock network delay ;
; 4.275 ; 2.020 ; ; ; ; ; data path ;
; 2.255 ; 0.000 ; ; uTco ; 1 ; DDIOOUTCELL_X48_Y0_N50 ; test_out~reg0 ;
; 2.979 ; 0.724 ; RR ; CELL ; 1 ; DDIOOUTCELL_X48_Y0_N50 ; test_out~reg0|q ;
; 2.979 ; 0.000 ; RR ; IC ; 1 ; IOOBUF_X48_Y0_N42 ; test_out~output|i ;
; 4.275 ; 1.296 ; RR ; CELL ; 1 ; IOOBUF_X48_Y0_N42 ; test_out~output|o ;
; 4.275 ; 0.000 ; RR ; CELL ; 0 ; PIN_AN17 ; test_out ;
+---------+---------+----+------+--------+------------------------+---------------------+
+-------------------------------------------------------------------------+
; Data Required Path ;
+---------+---------+----+------+--------+----------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+----------+---------------------+
; 0.000 ; 0.000 ; ; ; ; ; latch edge time ;
; 0.000 ; 0.000 ; ; ; ; ; clock path ;
; 0.000 ; 0.000 ; R ; ; ; ; clock network delay ;
; 0.000 ; 0.000 ; ; ; ; ; clock uncertainty ;
; 3.000 ; 3.000 ; R ; oExt ; 0 ; PIN_AN17 ; test_out ;
+---------+---------+----+------+--------+----------+---------------------+
This analysis is similar to the max output delay, only it’s calculated against the same clock edge (and not the following one).
As before, the data path continues the clock path until the physical output is stable, calculated at 4.275 ns.
This is compared with the time of the same clock at 0 ns, minus the output delay. Recall that the min output delay was negative (-3 ns), which is why it appears as a positive number in the calculation.
Conclusion: Data was stable until 4.275 ns, and needs to be stable until 3 ns. That’s fine, with a 1.275 ns slack.
This demonstrates why set_output_delay -min is minus the hold time of the receiver: The given output delay with reversed sign is used as the time which the data path delay must exceed. In other words, the data must be stable for that long after the clock. This is the definition of hold time.
OK, what’s this?
This page is the example part of another post, which explains the meaning of set_input_delay and set_output_delay in SDC timing constraints.
As mentioned on the other post, the relevant timing constraints were:
create_clock -name theclk -period 20 [get_ports test_clk]
set_output_delay -clock theclk -max 8 [get_ports test_out]
set_output_delay -clock theclk -min -3 [get_ports test_out]
set_input_delay -clock theclk -max 4 [get_ports test_in]
set_input_delay -clock theclk -min 2 [get_ports test_in]
set_input_delay -max timing analysis (setup)
Slack (MET) : 15.664ns (required time - arrival time)
Source: test_in
(input port clocked by theclk {rise@0.000ns fall@10.000ns period=20.000ns})
Destination: test_samp_reg/D
(rising edge-triggered cell FDRE clocked by theclk {rise@0.000ns fall@10.000ns period=20.000ns})
Path Group: theclk
Path Type: Setup (Max at Fast Process Corner)
Requirement: 20.000ns (theclk rise@20.000ns - theclk rise@0.000ns)
Data Path Delay: 2.465ns (logic 0.291ns (11.797%) route 2.175ns (88.203%))
Logic Levels: 1 (IBUF=1)
Input Delay: 4.000ns
Clock Path Skew: 2.162ns (DCD - SCD + CPR)
Destination Clock Delay (DCD): 2.162ns = ( 22.162 - 20.000 )
Source Clock Delay (SCD): 0.000ns
Clock Pessimism Removal (CPR): 0.000ns
Clock Uncertainty: 0.035ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
Total System Jitter (TSJ): 0.071ns
Total Input Jitter (TIJ): 0.000ns
Discrete Jitter (DJ): 0.000ns
Phase Error (PE): 0.000ns
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock theclk rise edge) 0.000 0.000 r
input delay 4.000 4.000
AE20 0.000 4.000 r test_in (IN)
net (fo=0) 0.000 4.000 test_in
AE20 IBUF (Prop_ibuf_I_O) 0.291 4.291 r test_in_IBUF_inst/O
net (fo=1, routed) 2.175 6.465 test_in_IBUF
SLICE_X0Y1 FDRE r test_samp_reg/D
------------------------------------------------------------------- -------------------
(clock theclk rise edge) 20.000 20.000 r
AE23 0.000 20.000 r test_clk (IN)
net (fo=0) 0.000 20.000 test_clk
AE23 IBUF (Prop_ibuf_I_O) 0.077 20.077 r test_clk_IBUF_inst/O
net (fo=1, routed) 1.278 21.355 test_clk_IBUF
BUFGCTRL_X0Y4 BUFG (Prop_bufg_I_O) 0.026 21.381 r test_clk_IBUF_BUFG_inst/O
net (fo=2, routed) 0.781 22.162 test_clk_IBUF_BUFG
SLICE_X0Y1 FDRE r test_samp_reg/C
clock pessimism 0.000 22.162
clock uncertainty -0.035 22.126
SLICE_X0Y1 FDRE (Setup_fdre_C_D) 0.003 22.129 test_samp_reg
-------------------------------------------------------------------
required time 22.129
arrival time -6.465
-------------------------------------------------------------------
slack 15.664
This analysis starts at time zero, adds the 4 ns (clock-to-output) that was specified in the max input delay constraint, and continues that data path at the fastest possible combination of process, voltage and temperature. Together with the FPGA’s own data path delay (2.465 ns), the total data path delay stands at 6.465 ns.
The clock path is the calculated, once again with the fastest possible combination, starting from the following clock at 20 ns. The clock travels from the input pin to the flip-flop (with no clock network delay compensation, since no PLL is involved), taking into account the calculated jitter. All in all, the clock path ends at 22.129 ns, which is 15.664 ns after the data arrived to the flip-flop, which is this constraint’s slack.
It’s simple to see from this analysis that the max input delay is the clock-to-output ( + board delay), as it’s added to the data path. So it’s basically how late the data path started. Note the “Max” part in the Path Type above.
set_input_delay -min timing analysis (hold)
Min Delay Paths
--------------------------------------------------------------------------------------
Slack (VIOLATED) : -0.045ns (arrival time - required time)
Source: test_in
(input port clocked by theclk {rise@0.000ns fall@10.000ns period=20.000ns})
Destination: test_samp_reg/D
(rising edge-triggered cell FDRE clocked by theclk {rise@0.000ns fall@10.000ns period=20.000ns})
Path Group: theclk
Path Type: Hold (Min at Slow Process Corner)
Requirement: 0.000ns (theclk rise@0.000ns - theclk rise@0.000ns)
Data Path Delay: 3.443ns (logic 0.626ns (18.194%) route 2.817ns (81.806%))
Logic Levels: 1 (IBUF=1)
Input Delay: 2.000ns
Clock Path Skew: 5.351ns (DCD - SCD - CPR)
Destination Clock Delay (DCD): 5.351ns
Source Clock Delay (SCD): 0.000ns
Clock Pessimism Removal (CPR): -0.000ns
Clock Uncertainty: 0.035ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
Total System Jitter (TSJ): 0.071ns
Total Input Jitter (TIJ): 0.000ns
Discrete Jitter (DJ): 0.000ns
Phase Error (PE): 0.000ns
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock theclk rise edge) 0.000 0.000 r
input delay 2.000 2.000
AE20 0.000 2.000 r test_in (IN)
net (fo=0) 0.000 2.000 test_in
AE20 IBUF (Prop_ibuf_I_O) 0.626 2.626 r test_in_IBUF_inst/O
net (fo=1, routed) 2.817 5.443 test_in_IBUF
SLICE_X0Y1 FDRE r test_samp_reg/D
------------------------------------------------------------------- -------------------
(clock theclk rise edge) 0.000 0.000 r
AE23 0.000 0.000 r test_clk (IN)
net (fo=0) 0.000 0.000 test_clk
AE23 IBUF (Prop_ibuf_I_O) 0.734 0.734 r test_clk_IBUF_inst/O
net (fo=1, routed) 2.651 3.385 test_clk_IBUF
BUFGCTRL_X0Y4 BUFG (Prop_bufg_I_O) 0.093 3.478 r test_clk_IBUF_BUFG_inst/O
net (fo=2, routed) 1.873 5.351 test_clk_IBUF_BUFG
SLICE_X0Y1 FDRE r test_samp_reg/C
clock pessimism 0.000 5.351
clock uncertainty 0.035 5.387
SLICE_X0Y1 FDRE (Hold_fdre_C_D) 0.101 5.488 test_samp_reg
-------------------------------------------------------------------
required time -5.488
arrival time 5.443
-------------------------------------------------------------------
slack -0.045
This analysis starts at time zero, adds the 2 ns (clock-to-output) that was specified in the min input delay constraint, and continues that data path at the slowest possible combination of process, voltage and temperature. Together with the FPGA’s own data path delay (3.443 ns), the total data path delay stands at 5.443 ns. It should be no surprise that the FPGA’s own delay is bigger compared with the fast analysis above.
The clock path is the calculated, now with the slowest possible combination, starting from the same clock edge at 0 ns. After all, this is a hold calculation, so the question is whether the mat wasn’t swept under the feet of the sampling flip-flop before it managed to sample it.
The clock travels from the input pin to the flip-flop (with no clock network delay compensation, since no PLL is involved), taking into account the calculated jitter. All in all, the clock path ends at 5.488 ns, which is 0.045 ns too late after the data switched. So the constraint was violated, with a negative slack of 0.045.
It’s simple to see from this analysis that the min input delay is the minimal clock-to-output, as it’s added to the data path. So it’s basically how early the data path may start. Note the “Min” part in the Path Type above.
It may come as a surprise that a 2 ns clock-to-output can violate a hold constraint. This shouldn’t be taken lightly — it can cause real problems.
The solution for this case would be to add a PLL to the clock path, which locks the global network’s clock to the input clock. This effectively means pulling it several nanoseconds earlier, which definitely solves the problem.
set_output_delay -max timing analysis (setup)
Slack (MET) : 2.983ns (required time - arrival time)
Source: test_out_reg/C
(rising edge-triggered cell FDRE clocked by theclk {rise@0.000ns fall@10.000ns period=20.000ns})
Destination: test_out
(output port clocked by theclk {rise@0.000ns fall@10.000ns period=20.000ns})
Path Group: theclk
Path Type: Max at Slow Process Corner
Requirement: 20.000ns (theclk rise@20.000ns - theclk rise@0.000ns)
Data Path Delay: 3.631ns (logic 2.583ns (71.152%) route 1.047ns (28.848%))
Logic Levels: 1 (OBUF=1)
Output Delay: 8.000ns
Clock Path Skew: -5.351ns (DCD - SCD + CPR)
Destination Clock Delay (DCD): 0.000ns = ( 20.000 - 20.000 )
Source Clock Delay (SCD): 5.351ns
Clock Pessimism Removal (CPR): 0.000ns
Clock Uncertainty: 0.035ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
Total System Jitter (TSJ): 0.071ns
Total Input Jitter (TIJ): 0.000ns
Discrete Jitter (DJ): 0.000ns
Phase Error (PE): 0.000ns
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock theclk rise edge) 0.000 0.000 r
AE23 0.000 0.000 r test_clk (IN)
net (fo=0) 0.000 0.000 test_clk
AE23 IBUF (Prop_ibuf_I_O) 0.734 0.734 r test_clk_IBUF_inst/O
net (fo=1, routed) 2.651 3.385 test_clk_IBUF
BUFGCTRL_X0Y4 BUFG (Prop_bufg_I_O) 0.093 3.478 r test_clk_IBUF_BUFG_inst/O
net (fo=2, routed) 1.873 5.351 test_clk_IBUF_BUFG
SLICE_X0Y1 FDRE r test_out_reg/C
------------------------------------------------------------------- -------------------
SLICE_X0Y1 FDRE (Prop_fdre_C_Q) 0.223 5.574 r test_out_reg/Q
net (fo=1, routed) 1.047 6.622 test_out_OBUF
AK21 OBUF (Prop_obuf_I_O) 2.360 8.982 r test_out_OBUF_inst/O
net (fo=0) 0.000 8.982 test_out
AK21 r test_out (OUT)
------------------------------------------------------------------- -------------------
(clock theclk rise edge) 20.000 20.000 r
clock pessimism 0.000 20.000
clock uncertainty -0.035 19.965
output delay -8.000 11.965
-------------------------------------------------------------------
required time 11.965
arrival time -8.982
-------------------------------------------------------------------
slack 2.983
Since the purpose of this analysis is to measure the output delay, it starts off with the clock edge, follows it towards the flip-flop, and then along the data path. That sums up to the overall delay. Note that the “Path Type” doesn’t say it’s a setup calculation (to avoid confusion?) even though it takes the following clock (at 20 ns) into consideration.
The calculation takes place at the slowest possible combination of process, voltage and temperature (recall that the input setup calculation took place with the fastest one). Following the clock path, it’s evidently very similar to the clock path of the hold analysis for input delay, which is quite expected, as both are based upon the slow model.
The data path simply continues the clock path until the physical output is stable, calculated at 8.982 ns.
This is compared with the time of the following clock at 20 ns, minus the output delay. Minus the possible jitter (0.035 ns in the case above). Data arrived at 8.982 ns, the moment that counts is at ~12 ns, so there’s almost 3 ns slack.
This demonstrates why set_output_delay -max is the setup time of the receiver: The output delay is reduced from the following clock’s time position, and that’s the goal to meet. That’s exactly the definition of setup time: How long before the following clock the data must be stable.
set_output_delay -min timing analysis (hold)
Slack (MET) : 0.791ns (arrival time - required time)
Source: test_out_reg/C
(rising edge-triggered cell FDRE clocked by theclk {rise@0.000ns fall@10.000ns period=20.000ns})
Destination: test_out
(output port clocked by theclk {rise@0.000ns fall@10.000ns period=20.000ns})
Path Group: theclk
Path Type: Min at Fast Process Corner
Requirement: 0.000ns (theclk rise@0.000ns - theclk rise@0.000ns)
Data Path Delay: 1.665ns (logic 1.384ns (83.159%) route 0.280ns (16.841%))
Logic Levels: 1 (OBUF=1)
Output Delay: -3.000ns
Clock Path Skew: -2.162ns (DCD - SCD - CPR)
Destination Clock Delay (DCD): 0.000ns
Source Clock Delay (SCD): 2.162ns
Clock Pessimism Removal (CPR): -0.000ns
Clock Uncertainty: 0.035ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
Total System Jitter (TSJ): 0.071ns
Total Input Jitter (TIJ): 0.000ns
Discrete Jitter (DJ): 0.000ns
Phase Error (PE): 0.000ns
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock theclk rise edge) 0.000 0.000 r
AE23 0.000 0.000 r test_clk (IN)
net (fo=0) 0.000 0.000 test_clk
AE23 IBUF (Prop_ibuf_I_O) 0.077 0.077 r test_clk_IBUF_inst/O
net (fo=1, routed) 1.278 1.355 test_clk_IBUF
BUFGCTRL_X0Y4 BUFG (Prop_bufg_I_O) 0.026 1.381 r test_clk_IBUF_BUFG_inst/O
net (fo=2, routed) 0.781 2.162 test_clk_IBUF_BUFG
SLICE_X0Y1 FDRE r test_out_reg/C
------------------------------------------------------------------- -------------------
SLICE_X0Y1 FDRE (Prop_fdre_C_Q) 0.100 2.262 r test_out_reg/Q
net (fo=1, routed) 0.280 2.542 test_out_OBUF
AK21 OBUF (Prop_obuf_I_O) 1.284 3.826 r test_out_OBUF_inst/O
net (fo=0) 0.000 3.826 test_out
AK21 r test_out (OUT)
------------------------------------------------------------------- -------------------
(clock theclk rise edge) 0.000 0.000 r
clock pessimism 0.000 0.000
clock uncertainty 0.035 0.035
output delay 3.000 3.035
-------------------------------------------------------------------
required time -3.035
arrival time 3.826
-------------------------------------------------------------------
slack 0.791
This analysis is similar to the max output delay, only it’s calculated on the fastest possible combination of process, voltage and temperature, and against the same clock edge (and not the following one). So again, going from setup to hold, these are reversed. Once again, the clock path is very similar to the clock path of the setup analysis for input delay, which is quite expected, as both are based upon the fast model.
As before, the data path continues the clock path until the physical output is stable, calculated at 3.826 ns (note the difference with the slow path!).
This is compared with the time of the same clock at 0 ns, minus the output delay, minus the possible jitter (0.035 ns in the case above, not clear why it’s counted if it’s the same clock cycle, but anyhow). Recall that the min output delay was negative (-3 ns), which is why it appears as a positive number in the calculation.
Conclusion: Data was stable until 3.826 ns, and needs to be stable until 3.035. That’s fine, with a 0.791 ns slack.
This demonstrates why set_output_delay -min is minus the hold time of the receiver: Jitter aside, the given output delay with reversed sign is used as the time which the data path delay must exceed. In other words, the data must be stable for that long after the clock. This is the definition of hold time.
Introduction
Synopsys Design Constraints (SDC) has been adopted by Xilinx (in Vivado, as .xdc files) as well as Altera (in Quartus, as .sdc files) and other FPGA vendors as well. Despite the wide use of this format, there seems to be some confusion regarding the constraints for defining I/O timing.
This post is defines what they mean, and then shows the timing calculations made by Vivado and Quartus (in separate pages), demonstrating their meaning when implementing a very simple example design. So there’s no need to take my word for it, and this also gives a direction on how to check that your own constraints did what they were supposed to do.
There are several options to these constraints, but these are documented elsewhere. This post is about the basics.
And yes, it’s the same format with Xilinx and Altera. Compatibility. Unbelievable, but true.
What they mean
In short,
- set_input_delay -clock … -max … : The maximal clock-to-output of the driving chip + board propagation delay
- set_input_delay -clock … -min … : The minimal clock-to-output of the driving chip. If not given, choose zero (maybe a future revision of the driving chip will be manufactured with a really fast process)
- set_output_delay -clock … -max … : The t_setup time of the receiving chip + board propagation delay
- set_output_delay -clock … -min … : Minus the t_hold time of the receiving chip (e.g. set to -1 if the hold time is 1 ns).
Note that if neither -min or -max are given, it’s like two assignments, one with -min and one with -max. In other words: Poor constraining.
The definitions are confusing: set_input_delay defines the allowed range of delays of the data toggle after a clock, but set_output_delay defines the range of delays of the clock after a data toggle. Presumably, the rationale behind this is to match datasheet figures of the device on the other end.
Always constraint both min and max
It may seem meaningless to use the min/max constraints. For example, using a catch-both single set_output_delay sets the setup time correctly, and the hold time to a negative value which is incorrect, but why bother? It allows the output port to toggle before the clock, but that couldn’t happen, could it?
Well, actually it can. For example, it’s quite common to let an FPGA PLL (or alike) generate the internal FPGA clock from the clock at some input pin (the “clock on the board”). This allows the PLL to align the clock on the FPGA’s internal clock network to the input clock, by time-shifting it slightly to compensate for the delay of the clock distribution network.
Actually, the implementation tools may feel free to shift the clock to slightly earlier than the clock input, in order to meet timing better: A slow path from logic to output may violate the maximal delay allowed from clock to output. Moving the clock earlier fixes this. But moving the internal clock to earlier than the clock on the board may switch other outputs that depend on the same clock to before the clock on the board toggles, leading to hold time violations on the receiver of these outputs. Nothing prevents this from happening, except a min output delay constraint.
Outline of example design
We’ll assume test_clk input clock, test_in input pin, and test_out output, with the following relationship:
always @(posedge test_clk)
begin
test_samp <= test_in;
test_out <= test_samp;
end
No PLL is used to align the internal clock with the board’s test_clk, so there’s a significant clock delay.
And the following timing constraints applied in the SDC/XDC file:
create_clock -name theclk -period 20 [get_ports test_clk]
set_output_delay -clock theclk -max 8 [get_ports test_out]
set_output_delay -clock theclk -min -3 [get_ports test_out]
set_input_delay -clock theclk -max 4 [get_ports test_in]
set_input_delay -clock theclk -min 2 [get_ports test_in]
As the tools’ timing calculations are rather long, they are on separate pages:
Often I prefer to handle I/O timing simply by ensuring that all registers are pushed into the I/O cells. Where timing matters, that is.
It seems like I/O register packing isn’t the default in Quartus. Anyhow, here’s the lazy man’s recipe for this scenario.
In a previous version of this post, I suggested to disable timing checking on all I/Os. This silences the unconstrained path warning during implementation, and in particular prevents the “TimeQuest Timing Analyzer” section in Quartus’ reports pane turning red:
set_false_path -from [get_ports]
set_false_path -to [get_ports]
This isn’t such a good idea, it turns out, in particular regarding input ports. This is elaborated further below.
Nevertheless, one needs to convince the fitter to push registers into the I/O block. In the QSF, add
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to *
set_instance_assignment -name FAST_INPUT_REGISTER ON -to *
set_instance_assignment -name FAST_OUTPUT_ENABLE_REGISTER ON -to *
It’s somewhat aggressive to assign these assignments to absolutely everything, but it does the job. The fitter issues warnings for the I/O elements it fails to enforce these constraints on, which is actually a good thing.
To see how well it went, look in the “Resource Section” of the fitter report (possibly find it in Quartus’ reports pane) and look for “Input Registers” etc., whatever applies.
The difference is evident in timing reports of paths involving I/O cells. For example, compare this path which involves an I/O register:
+----------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+---------+----+------+--------+-----------------------+-----------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+-----------------------+-----------------+
; 2.918 ; 2.918 ; ; ; ; ; data path ;
; 0.000 ; 0.000 ; ; ; 1 ; DDIOOUTCELL_X3_Y0_N32 ; rst ;
; 0.465 ; 0.465 ; RR ; CELL ; 1 ; DDIOOUTCELL_X3_Y0_N32 ; rst|q ;
; 0.465 ; 0.000 ; RR ; IC ; 1 ; IOOBUF_X3_Y0_N30 ; RESETB~output|i ;
; 2.918 ; 2.453 ; RR ; CELL ; 1 ; IOOBUF_X3_Y0_N30 ; RESETB~output|o ;
; 2.918 ; 0.000 ; RR ; CELL ; 0 ; PIN_P3 ; RESETB ;
+---------+---------+----+------+--------+-----------------------+-----------------+
Note the DDIOOUTCELL element, and the zero increment in the routing between the register and the IOOBUF.
For comparison, here’s a path for which an I/O register wasn’t applied (prevented by logic):
+--------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+---------+----+------+--------+-----------------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+-----------------+---------------------+
; 8.284 ; 8.284 ; ; ; ; ; data path ;
; 0.000 ; 0.000 ; ; ; 1 ; FF_X3_Y0_N17 ; Dir_flop_sig ;
; 0.496 ; 0.496 ; RR ; CELL ; 8 ; FF_X3_Y0_N17 ; Dir_flop_sig|q ;
; 2.153 ; 1.657 ; RR ; IC ; 1 ; IOOBUF_X3_Y0_N9 ; DATA[7]~output|oe ;
; 8.284 ; 6.131 ; RF ; CELL ; 1 ; IOOBUF_X3_Y0_N9 ; DATA[7]~output|o ;
; 8.284 ; 0.000 ; FF ; CELL ; 1 ; PIN_T3 ; DATA[7] ;
+---------+---------+----+------+--------+-----------------+---------------------+
Here we see how a general-purpose flip-flop generates the signal, leading to routing of 1.657 ns. The main problem is that this routing delay will be different each implementation, so if there’s a signal integrity issue with the board, the FPGA might be blamed for it, since different FPGA versions seem to fix the problem or make it reappear.
Timing constraints
Both input and output ports should be tightly constrained, so they can’t be met other than making the best of I/O registers. Not only will this generate a timing failure if something goes wrong with the desired register packing, but it’s also necessary to achieve the minimal input-to-register timing, as explained next.
The discussion below applies only when the clock that drives the registers is directly related to an external clock (i.e. with a PLL that doesn’t multiply it with some exotic ratio). If the driving clock is practically unrelated to the external clock, things get significantly more complicated, as discussed in this post.
To demonstrate this issue, consider the following Verilog snippet:
module top
(
input clk,
input in,
output reg out
);
reg in_d, in_d2;
wire pll_clk;
always @(posedge pll_clk)
begin
in_d <= in;
in_d2 <= in_d;
out <= in_d2;
end
/* Here comes an instantiation of a phase-compensating PLL, which
doesn't change the frequency */
endmodule
with the following constraint in the SDC file
create_clock -name main_clk -period 10 -waveform { 0 5 } [get_ports {clk}]
derive_pll_clocks
derive_clock_uncertainty
set_input_delay -clock main_clk -max 8.5 [get_ports in*]
set_input_delay -clock main_clk -min 0 [get_ports in*]
As explained on this post, set_input_delay is the maximal delay of the source of the signal, from clock to a valid logic state. Since the clock’s period is set to 10 ns, setting the delay constraint to 8.5 ns leaves 1.5 ns until the following clock arrives (at 10 ns). In other words, the setup time on the FPGA pin is constrained not to exceed 1.5 ns.
Note that set_max_delay can be used as well for this purpose (in some cases it’s the only way) as discussed in this post.
Compiling this (along with the FAST_INPUT_REGISTER ON QSF assignment shown above) yields the following segment in the timing report:
+----------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+---------+----+------+--------+-------------------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+-------------------+---------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 0.000 ; 0.000 ; ; ; ; ; clock path ;
; 0.000 ; 0.000 ; R ; ; ; ; clock network delay ;
; 8.500 ; 8.500 ; F ; iExt ; 1 ; PIN_F2 ; in ;
; 9.550 ; 1.050 ; ; ; ; ; data path ;
; 8.500 ; 0.000 ; FF ; IC ; 1 ; IOIBUF_X0_Y22_N15 ; in~input|i ;
; 9.308 ; 0.808 ; FF ; CELL ; 1 ; IOIBUF_X0_Y22_N15 ; in~input|o ;
; 9.308 ; 0.000 ; FF ; IC ; 1 ; FF_X0_Y22_N17 ; in_d|d ;
; 9.550 ; 0.242 ; FF ; CELL ; 1 ; FF_X0_Y22_N17 ; in_d ;
+---------+---------+----+------+--------+-------------------+---------------------+
Unlike the output register, there is no “DDIOINCELL” flip-flop listed, but what appears to be a regular flip-flop. However note that the interconnect to this flip-flop has zero delay (marked in red), which is a clear indication that the flip-flop and input buffer are fused together.
The datasheet report for this input goes:
+---------------------------------------------------------------------------------------------------+
; Setup Times ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
; Data Port ; Clock Port ; Rise ; Fall ; Clock Edge ; Clock Reference ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
; in ; main_clk ; 1.282 ; 1.461 ; Rise ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
+-----------------------------------------------------------------------------------------------------+
; Hold Times ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; Data Port ; Clock Port ; Rise ; Fall ; Clock Edge ; Clock Reference ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; in ; main_clk ; -0.683 ; -0.862 ; Rise ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
As required, the setup time required by the FPGA is lower than the 1.5 ns limit set by the constraint.
Now let’s loosen the input setup delay by 2 ns, leave everything else as it was, and rerun the compilation:
set_input_delay -clock main_clk -max 6.5 [get_ports in*]
set_input_delay -clock main_clk -min 0 [get_ports in*]
The segment in the timing report is now:
+----------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+---------+----+------+--------+-------------------+---------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+-------------------+---------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 0.000 ; 0.000 ; ; ; ; ; clock path ;
; 0.000 ; 0.000 ; R ; ; ; ; clock network delay ;
; 6.500 ; 6.500 ; F ; iExt ; 1 ; PIN_F2 ; in ;
; 8.612 ; 2.112 ; ; ; ; ; data path ;
; 6.500 ; 0.000 ; FF ; IC ; 1 ; IOIBUF_X0_Y22_N15 ; in~input|i ;
; 7.308 ; 0.808 ; FF ; CELL ; 1 ; IOIBUF_X0_Y22_N15 ; in~input|o ;
; 8.370 ; 1.062 ; FF ; IC ; 1 ; FF_X0_Y22_N17 ; in_d|d ;
; 8.612 ; 0.242 ; FF ; CELL ; 1 ; FF_X0_Y22_N17 ; in_d ;
+---------+---------+----+------+--------+-------------------+---------------------+
Huh? The interconnect suddenly rose to 1.062 ns?! Note that the placement of the register didn’t change, so there’s no doubt that in_d is an I/O register. So where did this delay come from?
To answer this, a closer look on the design is required. After a full compilation and selecting Tools > Netlist Viewers > Technology Map Viewer (Post-Fitting), the following diagram appears (partly shown below, click to enlarge):
Right-clicking in_d (the register) and selecting Locate Note > Locate in Resource Property Editor reveals the following (click to enlarge):
To the right of this drawing (not shown above), the property “Input Pin to Input Register Delay” is set to 2. This is the reason for the delay. Before the constraint was loosened up, it was set to 0. The immediate lesson is:
If the setup constraint isn’t set to the technology’s best possible value, Quartus may add a delay on its expense.
But why, Quartus, why?
So one may wonder why Quartus inserts this delay between the input pad and the register. Wasn’t the whole point to sample as soon as possible? To answer this, let’s look at the updated datasheet report:
---------------------+
; Data Port ; Clock Port ; Rise ; Fall ; Clock Edge ; Clock Reference ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
; in ; main_clk ; 2.205 ; 2.523 ; Rise ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
+-----------------------------------------------------------------------------------------------------+
; Hold Times ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; Data Port ; Clock Port ; Rise ; Fall ; Clock Edge ; Clock Reference ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; in ; main_clk ; -1.570 ; -1.882 ; Rise ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
Recall that 2 ns were reduced from the delay constraint, hence the maximal allowed setup time went up from 1.5 ns to 3.5 ns. It’s easy to see that this requirement is met, with a slack of almost 1 ns.
So what Quartus did was saying “I can meet the setup requirement easily, with a spare of 2 ns. Let’s give 1 ns extra to the setup time, and one 1 ns to the hold time requirement (which is 0 ns)”. And indeed, by adding this 1.062 ns delay, the hold time improved from -0.683 ns to -1.570 ns (and please don’t pick on me on why the difference isn’t exact).
Bottom line: Quartus widened the margin for both setup and hold, making the input more robust to jitter. While this is a rather sensible thing to do, this is often not desired nor expected to happen.
Conclusion: If you want to get the absolutely minimal delay from the input to the register, run a compilation with a delay constraint that fails, and then loosen the constraint just enough to resolve this failure. This ensures Quartus won’t try to “improve” the timing by adding this input delay for the sake of a better hold time.
Using DDR primitives
Intel’s FPGAs have dedicated logic on or near the I/O cells to allow for DDR output and sampling, as detailed in the relevant user guide, ug_altddio.pdf. Instantiating such (or using the ALTDDIO_BIDIR megafunction) is an appealing way to force the tools into pushing the register(s) into the I/O cells. Spoiler: It’s not necessarily a good idea.
For example, instantiating something like
altddio_bidir ioddr
(
.padio(pin),
.aclr (1'b0),
.datain_h(datain_h),
.datain_l(datain_l),
.inclock(clk),
.oe(oe),
.outclock(clk),
.dataout_h(dataout_h),
.dataout_l(dataout_l),
.oe_out (),
.aset (1'b0),
.combout(),
.dqsundelayedout(),
.inclocken(1'b1),
.outclocken(1'b1),
.sclr(1'b0),
.sset(1'b0));
defparam
ioddr.extend_oe_disable = "OFF",
ioddr.implement_input_in_lcell = "OFF",
ioddr.intended_device_family = "Cyclone IV E",
ioddr.invert_output = "OFF",
ioddr.lpm_hint = "UNUSED",
ioddr.lpm_type = "altddio_bidir",
ioddr.oe_reg = "REGISTERED",
ioddr.power_up_high = "OFF",
ioddr.width = 1;
indeed results in logic that implements bidirectional DDR interface, but it’s a partial success as far as timing is concerned, at least on Cyclone IV: While the clock-to-output timing is exactly the same as a plain output register that is packet into the I/O cell, the delay on the input path is actually worse with the instantiation above. YMMV with other Intel FPGA families.
Note that in order to mimic plain SDR registers with a DDR primitive, its datain_h and datain_l ports must be connected to the same wire, so the clock’s falling edge doesn’t change anything. Likewise, the dataout_l port’s value should be ignored, as it’s sampled on the falling edge. Also note that the output enable port (oe) is an SDR input — as far as I can understand, it’s not possible to go on and off high-Z in DDR rate with Intel FPGAs. At least not natively.
Now to why it worked nicely on the output registers, and not with the input: The hint is in the timing reports above: Even for a plain I/O cell register, a DDIOOUTCELL_Xn_Ym_Nk component is the register used. In other words, the DDR output register is used even for single-rate outputs, but only with one clock edge. As for the input path, the timing reports above show that a logic fabric register (FF_Xn_Ym_Nk) is used. And here’s the crux: The DDR input logic is implemented in fabric as well, and to make it worse, combinatoric blocks are squeezed between the I/O cell and the flip-flop in the DDR case. Frankly, I don’t understand why, because these combinatoric blocks are just single-input-single-output passthroughs.
These observations are backed by timing reports as well as the drawings displayed by Quartus’ Post-Fit Technology Map Viewer. In particular those useless combinatoric blocks.
This entire issue most likely varies from one FPGA family to another. As for Cyclone IV, it only makes sense to use DDR primitives for outputs.
Even more important, the fact that a DDR primitive output uses identical logic as an packed output register allows producing an output clock that is aligned with the the other outputs: Feed a DDR output primitive with constant ’1′ and ’0′ on the datain_h and datain_l ports, respectively, and apply plain output register packing for the other outputs. The toggling of the other outputs is aligned to the rising edge of clock that comes from the DDR output.
Well, almost. The timing analysis of a output clock is different, because the clock toggles a mux that selects which of the two output registers feeds the output (scroll horizontally for the details):
+------------------------------------------------------------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+---------+----+------+--------+-------------------------+-----------------------------------------------------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+-------------------------+-----------------------------------------------------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 0.000 ; 0.000 ; ; ; ; ; clock path ;
; 0.000 ; 0.000 ; R ; ; ; ; clock network delay ;
; 0.000 ; 0.000 ; R ; ; 1 ; PIN_B12 ; osc_clock ;
; 5.610 ; 5.610 ; ; ; ; ; data path ;
; 0.000 ; 0.000 ; RR ; IC ; 1 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|i ;
; 0.667 ; 0.667 ; RR ; CELL ; 2 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|o ;
; 0.853 ; 0.186 ; RR ; IC ; 1 ; CLKCTRL_G12 ; osc_clock~inputclkctrl|inclk[0] ;
; 0.853 ; 0.000 ; RR ; CELL ; 165 ; CLKCTRL_G12 ; osc_clock~inputclkctrl|outclk ;
; 1.971 ; 1.118 ; RR ; IC ; 1 ; DDIOOUTCELL_X16_Y29_N11 ; sram_controller_ins|ddr_clk|auto_generated|ddio_outa[0]|muxsel ;
; 3.137 ; 1.166 ; RR ; CELL ; 1 ; DDIOOUTCELL_X16_Y29_N11 ; sram_controller_ins|ddr_clk|auto_generated|ddio_outa[0]|dataout ;
; 3.137 ; 0.000 ; RR ; IC ; 1 ; IOOBUF_X16_Y29_N9 ; sram_clk~output|i ;
; 5.610 ; 2.473 ; RR ; CELL ; 1 ; IOOBUF_X16_Y29_N9 ; sram_clk~output|o ;
; 5.610 ; 0.000 ; RR ; CELL ; 0 ; PIN_E10 ; sram_clk ;
+---------+---------+----+------+--------+-------------------------+-----------------------------------------------------------------;
Note that this isn’t a register-to-pin analysis, but clock-to-pin. A set_output_delay constraint constraint will include this path nevertheless. However a set_max_delay constraint from registers to ports, if used, won’t include this path, so it has to be handled separately. In other words, if set_max_delay is used, it has to be of the form:
set_max_delay -from [get_clocks main_clk] -to [get_ports sram_clk] 3.8
Now, compare this with another pin with the same voltage standard etc., only driven by a register:
+----------------------------------------------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+---------+----+------+--------+-------------------------+---------------------------------------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+---------+----+------+--------+-------------------------+---------------------------------------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 2.507 ; 2.507 ; ; ; ; ; clock path ;
; 0.000 ; 0.000 ; ; ; ; ; source latency ;
; 0.000 ; 0.000 ; ; ; 1 ; PIN_B12 ; osc_clock ;
; 0.000 ; 0.000 ; RR ; IC ; 1 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|i ;
; 0.667 ; 0.667 ; RR ; CELL ; 2 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|o ;
; 0.853 ; 0.186 ; RR ; IC ; 1 ; CLKCTRL_G12 ; osc_clock~inputclkctrl|inclk[0] ;
; 0.853 ; 0.000 ; RR ; CELL ; 165 ; CLKCTRL_G12 ; osc_clock~inputclkctrl|outclk ;
; 1.970 ; 1.117 ; RR ; IC ; 1 ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller_ins|dq_wr_data[6]|clk ;
; 2.507 ; 0.537 ; RR ; CELL ; 1 ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller:sram_controller_ins|dq_wr_data[6] ;
; 5.645 ; 3.138 ; ; ; ; ; data path ;
; 2.717 ; 0.210 ; ; uTco ; 1 ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller:sram_controller_ins|dq_wr_data[6] ;
; 3.182 ; 0.465 ; RR ; CELL ; 1 ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller_ins|dq_wr_data[6]|q ;
; 3.182 ; 0.000 ; RR ; IC ; 1 ; IOOBUF_X37_Y29_N9 ; sram_dq[6]~output|i ;
; 5.645 ; 2.463 ; RR ; CELL ; 1 ; IOOBUF_X37_Y29_N9 ; sram_dq[6]~output|o ;
; 5.645 ; 0.000 ; RR ; CELL ; 1 ; PIN_G14 ; sram_dq[6] ;
+---------+---------+----+------+--------+-------------------------+---------------------------------------------------;
The total clock-to-output time differs by no more than 35 ps, even though the latter path is completely different on the face of it. This isn’t a coincidence. The FPGA is clearly designed to produce this similarity. Specifically, the timing analysis above is slow 1200 mV at 100 degrees, but this small difference is consistent in the other analyzed conditions as well.
Introduction
This is a not-so-short tutorial which is intended to make the setup of a Live TV media center on Linux a bit easier, walking through the processing chain from the digital transmission signal to the picture on the screen. Quite naturally, things go from “general knowledge” to a bit more hands-on. The approach here is to understand what you’re doing, something one can avoid when things just work by themselves. If you’re reading this, odds are they didn’t.
I suggest starting with the command line tools, as they give a lot of low-level information as they run. Once a TV channel can be viewed with these, I further suggest using Tvheadend as the backend, as it’s reasonably complicated to work with, and leaves a lot of control to the frontend software.
I also suggest a frontend (Kodi) for everyday TV viewing, and a way to configure it.
All in all, getting this to work is often a rather tedious process. This isn’t all that bad if one learns a few things on the way.
The DVB frontend (“adapter”)
Often with a USB interface, the DVB frontend receives the digital signal and turns it into a stream of bytes. Inside, it typically consist of a tuner, a demodulator and a USB interface chip. Often there’s a demux as well, which is discussed further on.
- The tuner (the analog part) nails down a piece of the frequency spectrum with the digital signal in it, and makes it accessible to the demodulator. The signal may arrive from a simple UHF antenna, a satellite dish or from a cable network. In principle, there is no difference: It’s an analog signal that carries a bitstream of several Mb/s, and the tuner’s job is to bring the signal down to a known lower frequency, where the demodulator expects it to be. The tuner cares only about the signal’s center frequency, and possibly its bandwidth.
- The demodulator (the digital communication part) turns the analog signal from the tuner into a stream of digital bits. This is the most sophisticated part, which includes a significant portion of signal processing and also decoding of error-correcting codes (and, of course, correcting bit errors if such are found and are correctable). All this is hidden from the end user, so all the demodulator tells us is typically if it’s locked on the signal or not. There are quite a few things that need to get synchronized properly, but all we get is something like “works” or “doesn’t work”. The demodulator may also supply information about the signal strength, the S/N ratio and the PostBER, which is an estimation of the bit error rate obtained after fixing bit errors by virtue of the error correction code. This estimation is possible because all but a fraction of the bits are recovered correctly, so the demodulator knows what its input signal would look like without noise and distortions, and so it can also tell how much noise comes in. And the S/N ratio is calculated accordingly.
- The USB interface chip (the computer hardware part) is the less interesting part, but it’s what the computer sees. The driver is often named after it, even though its the other devices that are important. Its main functionalities are: Relaying the output of the demodulator to the computer via a bulk USB endpoint, and supplying means to control and read status from the demodulator and tuner, which is almost always done with an I2C/SMBus over USB kind of bridge. The I2C bus interface may also be used to download firmware to these two devices.
In a Linux system, the DVB adapter is represented with device files in /dev/dvb/adapter0/ (the index goes up if there are several of them). The notable file is /dev/dvb/adapter0/dvr0, from which the data stream is read, possibly with plain file I/O. In other words, when the adapter is set up, it’s possible to record a proper video clip with just “cat”, as shown in this post. /dev/dvb/adapter?/{frontend?,mux?} are used to control the device.
There may be other device files as well, such as net0 (for controlling network packet functionality) or ca0 for Conditional Access.
For more insight, this set of slides may come handy.
What’s in the trunk?
Assuming that the DVB frontend is tuned and locked on a digital signal, there will be data in MPEG-TS format flowing out from /dev/dvb/adapter0/dvr0. Almost always, there are several TV/radio channels on a single digital transmission signal: The data stream is used to pass several types of packets, which may contain MPEG video or audio data, or other related data.
The packets that contain MPEG video or audio data are marked with an identifier, PID. In fact, watching a TV program consists of
- Tuning the DVB adapter to a certain reception frequency, and lock its demodulator on the digital signal
- Filter out all packets belonging to a certain PID, and pass them on to an MPEG video decoder
- Same for another PID, and pass them on to an MPEG audio decoder
- If there are subtitles, there’s another PID to filter out, and pass on to a subtitle rendering mechanism
So all in all, it’s a lot of packets multiplexed into a single stream of data, and it’s the receiver’s job to fish out those of interest. In order to make it easier, packets containing information that organizes the PIDs into services, i.e. TV and radio channels, are also transmitted on the same stream (in dedicated packets).
The end of this post shows the output of a scan with the dvbv5-scan command-line utility. It lists the information obtained in a specific digital stream in an organized manner. One thing that may be surprising about this list, is that a single service (i.e. TV channel) may contain more than a single audio PID. Which isn’t all that odd, as some TV channels may have alternative sound tracks, e.g. in different languages.
There’s also the dvbsnoop utility, which shows and dissects the packets of an MPEG-TS stream. Only for those interested in the really gory details.
By the way, in Tvheadend’s terminology, the raw stream of data that arrives from the demodulator is called a mux. This is a highly confusing misnomer, which probably came from idea that the packets in the stream are multiplexed. To the reset of the world, a “mux” is the machine that takes data from several sources and turns them into a stream. Which brings us to:
Demultiplexing
Assuming that the PIDs of the video and audio streams of the desired TV channel are known, there are two possibilities to filter them out:
- Software demuxing: Tell the DVB adapter to send everything to the dvr0 device file, and fish out the matching packets with the media player (or some intermediate software). Actually, it simply means that the media player ignores all packets that don’t have the requested PIDs.
- Hardware demuxing: Using the e.g. /dev/dvb/adapter0/demux0 device file to command the adapter to pass through only packets with certain PIDs to the /dev/dvb/adapter0/dvr0 output.
Software demuxing is the preferred choice for the typical domestic use, as it allows viewing more than one TV program at a time (assuming that both programs are on the same digital stream). Hardware demuxing is useful for viewing (or recording) TV with command-line utilities (see these two posts for examples of command line sessions).
Watching TV for real
Command-line utilities (see this, this, and this) can indeed be used to hack together some very basic kit for watching TV, and they are priceless for understanding why things went wrong with the fancier tools (hint: Somehow things always get wrong when video is involved).
But for everyday use, it’s best to let a TV streaming backend talk with the hardware. It allows fancy media center software as well as command line utilities to easily access TV channels. As of March 2017, I found Tvheadend + Kodi to be the best combination. In Kodi, I went for PVR IPTV Simple Client rather than Tvheadend’s own front end, as I’ll explain below.
So let’s first understand this backend / frontend business, and I’ll take Tvheadend as an example.
Tvheadend (formerly HTS TVheadend) is a TCP/IP server, which takes control of the DVB adapter(s). And it listens to two TCP/IP ports:
- HTTP Port 9981: Plain browsers can connect to http://localhost:9981/ for administration and configuration + machine readable playlists and Electronic Program Guide on certain URLs (see below). But most important: Availability of all TV channels as MPEG-TS streams, in a protocol easily accessible by a lot of software, with a plain HTTP connection.
- HTSP Port 9982: Home Tv Streaming Protocol (invented for Tvheadend?), seems to be used only be a handful of Linux clients. It’s a one-stop shop for all TV related information, but my own experience was a bit lame. So I’ll leave this aside for now.
The installation of Tvheadend hence involves making it work with the DVB adapter (which is usually simple if everything works smoothly with the command line utilities) as well as setting up the server. It’s may not be all that easy (there are worse), but it’s worth the effort (maybe my own messy jots will help), because:
- It allows simultaneous view of several TV channels, if they’re on the same digital stream (i.e. muxed on the same frequency channel). Watch one channel waiting for the commercials to end on another…
- View TV from any computer on the (wireless?) LAN.
- Virtually any media playing software supports its output format, MPEG-TS. Including stuff running on Windows.
- There are a lot of other features (Electronic Program Guide via the web interface with a browser, recording), but I’m not sure if these belong to the backend. But they may be useful to some.
So let’s take a look on how the Tvheadend conveys the TV channels to its clients. For this, I’ll assume that Tvheadend is properly installed, has been set up (through the web interface) to tune on some TV channels, and that it allows access without user/password from localhost (it’s a convenient setting, and it’s safe at least for 127.0.0.1/32). And that all access is done from the localhost (even though it can be any computer with HTTP access and due permissions. If so, replace “localhost” in the examples below with the IP or domain name of the server).
But first…
A word on IPTV / HLS (not really important for DVB, actually)
We’ll make a small detour to IPTV or HLS (HTTP Live Streaming), because Tvheadend does something similar. IPTV is the commonly used name for TV channels broadcast over the internet, whether it’s live or video-on-demand like kind of broadcasts.
An IPTV/HLS stream is essentially an MPEG-TS stream, similar to the DVB stream on air or cable. In order to make its broadcast over the web easier, it’s cut into chunks, each a few seconds long. The cuts are made on packet boundaries, so each chunk is a legal MPEG-TS segment by itself. A plain concatenation of several subsequent chunks (with e.g. “cat”) makes a perfectly playable MPEG-TS clip. Or stream.
Now to the IPTV client: To start off, the IPTV client is given an initial playlist (as a file or a URL to download this playlist from). That playlist is an M3U file, with one or several URLs, usually a TV channel for each. When the client accesses one of these URLs to start showing a channel, it often receives another playlist, which redirects it to other URLs, which in turn might redirect it further, and so on. These playlists are often set up to allow the client to choose different paths, depending on desired bit rate, display resolution, encoding format etc.
Eventually, possibly after a few redirection hops, the client ends with receiving a playlist containing a list of chunks, so it has the information on where to fetch chunks of MPEG-TS segments from. It starts fetching these chunks, concatenates then, and plays the video stream.
The complicated part of the HLS protocol is the traveling around playlists until the list of chunks is found. Once there, it’s just a matter of downloading those chunks, concatenating, and treating them as a DVB stream.
Tvheadend’s IPTV-like interface
So M3U playlists is the name of the game. Tvheadend offers the TV and radio channels it exposes as an M3U playlist, available at http://localhost:9981/playlist . In my case (Israeli DVB-T), it starts like this:
#EXTM3U
#EXTINF:-1 tvg-id="38e914f04571f2a3f5c915872ba6e794",88FM
http://localhost:9981/stream/channelid/1880418616?ticket=B0E6E9AB06F41C13C0AEC87B7A88966BCBCCE8F4&profile=pass
#EXTINF:-1 tvg-id="219e62923848dac382ed7fcd35c4ed9e",Aleph
http://localhost:9981/stream/channelid/308452897?ticket=88F2FD731008F28454AB8FF7F75BF896FA1F9C7F&profile=pass
#EXTINF:-1 tvg-id="fd874e5286a13d161eb1fa011fb42731",Bet
http://localhost:9981/stream/channelid/1380878333?ticket=3BF6D86889B5DB3DFCAF2EF25D07894B653C3700&profile=pass
#EXTINF:-1 tvg-id="aae608ee725cad880781301f68592dbc",Ch 1
http://localhost:9981/stream/channelid/1846077098?ticket=116EEC70D4201D82BF2A0F1E9AB7387EC9E12D30&profile=pass
#EXTINF:-1 tvg-id="41d97066c9c97e348f83269a6b18e8e6",Ch 10
http://localhost:9981/stream/channelid/1718671681?ticket=757FAE1BA26561DAEDB659E707627366A5071B17&profile=pass
#EXTINF:-1 tvg-id="fc2a79daaca0afd9a39123d4d0305a1f",Ch 2
http://localhost:9981/stream/channelid/1517890300?ticket=BE2E496685F892A1036B3C982888D0778784BDB4&profile=pass
[ ... etc ... ]
After the #EXTM3U header, there are pair of lines for each channel: The first line contains information about the channel (in particular the display name) and the second is the URL for accessing the channel. Unlike HTS/IPTV, this isn’t a go-find-another-playlist, but it directs immediately to where the video stream can be obtained.
The “tvg-id” tag is not common in playlist files in general, and it pairs the channel with its appearance in the EPG (more about that later). If you don’t have it, you probably have an old version of Tvheadend, which doesn’t support the EPG trickery I’m going to show below.
As the URLs in the playlist are “for real”, a plain wget command can be used to record any of these channels. For example, recording from Channel 10:
$ wget -O mytvshow.ts 'http://localhost:9981/stream/channelid/1718671681?ticket=757FAE1BA26561DAEDB659E707627366A5071B17&profile=pass'
--2017-03-13 11:25:59-- http://localhost:9981/stream/channelid/1718671681?ticket=757FAE1BA26561DAEDB659E707627366A5071B17&profile=pass
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:9981... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [video/mp2t]
Saving to: ‘mytvshow.ts’
mytvshow.ts [ <=> ] 3.10M 334KB/s
This will run in principle forever until stopped with CTRL-C. The mytvshow.ts file can be played with VLC, mplayer, ffplay or any other reasonable media player.
These URLs to the channels don’t change once Tvheadend has been set up. It’s therefore possible to download the playlist once, edit away unwanted channels, reorder the list (noted the radio channels at the beginning of the playlist above?), possibly combine with “real” IPTV channels, and feed a media center player with the edited playlist file.
It’s also possible to give these URLs directly to VLC and other media players. Viewing multiple channels at once is as simple as opening several instances of VLC.
One word about what Tvheadend does behind the scenes. In response to the wget command above, the following went to /var/log/syslog:
Mar 13 11:25:59 tv tvheadend[6410]: mpegts: 538MHz in Idan Plus T - tuning on Realtek RTL2832 (DVB-T) : DVB-T #0
Mar 13 11:25:59 tv tvheadend[6410]: subscription: 0018: "HTTP" subscribing on channel "Ch 10", weight: 100, adapter: "Realtek RTL2832 (DVB-T) : DVB-T #0", network: "Idan Plus T", mux: "538MHz", provider: "Idan +", service: "Ch 10", profile="pass", hostname="127.0.0.1", client="Wget/1.17.1 (linux-gnu)"
Note that the HTTP connection resulted in a “subscription” to a certain channel within Tvheadend. This reflects the way Tvheadend mediates its resources, a single DVB adapter in this case, to fulfill requirements of subscribers requesting services.
Consequently, stopping the “recording” (pressing CTRL-C) resulted in
Mar 13 11:26:11 tv tvheadend[6410]: subscription: 0018: "HTTP" unsubscribing from "Ch 10", hostname="127.0.0.1", client="Wget/1.17.1 (linux-gnu)"
Needless to say, something similar happens when a media player opens a connection for streaming live TV.
EPG
A neat feature of DVB is that data for an Electronic Program Guide (EPG) is often embedded in the digital stream, so the name of the current program, along with a short description, is available when zapping to a new TV channel. As well as a TV guide to past and future programs, directly on the TV, shown neatly by the media center software.
There is probably no need to configure anything in Tvheadend to make this work. All those EPG grabbers available are tools for transferring information into Tvheadend, in its absence from the digital stream itself. In particular, if there’s satisfactory information in the “Electronic Program Guide” tab in Tvheadend’s web interface (http://localhost:9981/ with a browser), nothing needs to be fixed.
The common format for exchanging EPG information in Linux is XMLTV, which as its name implies, is in XML format. Tvheadend exports it at http://localhost:9981/xmltv or http://localhost:9981/xmltv/channels (accessing the former will cause an HTTP 302 redirection to the latter).
As of March 2017, this doesn’t work on Tvheadend versions available on the “stable” apt repositories. If attempting to access the URL for XMLTV from a browser results in “1 Unknown Code” appearing, an upgrade is required. Or no EPG will be available with the setup I suggest below.
An XMLTV file typically looks something like this:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE tv SYSTEM "xmltv.dtd">
<tv generator-info-name="TVHeadend-4.1-2405~geb495a0~xenial" source-info-name="tvh-Tvheadend">
<channel id="67a72084ee9a5ddb2fcd89129887bf78">
<display-name>Ch 99</display-name>
</channel>
<channel id="fa723385817605edc2b138d96c259b67">
<display-name>Ch 33</display-name>
</channel>
[ ... ]
<programme start="20170313113000 +0200" stop="20170313123000 +0200" channel="67a72084ee9a5ddb2fcd89129887bf78">
<title lang="heb">ועדה ש.ח כת' עב' כת' ער'</title>
<desc lang="heb">ועדה ש.ח כת' עב' כת' ער'
ועדה מיוחדת לפניות הציבור:
פניות ציבור בנושא התנהלות חברת החשמל בגביית תשלומים ומדיניות ניתוקי חשמל לצרכנים
</desc>
</programme>
<programme start="20170313121000 +0200" stop="20170313124500 +0200" channel="41d97066c9c97e348f83269a6b18e8e6">
<title lang="heb">ראש בראש כת' עב' </title>
<sub-title lang="heb">פרק 345</sub-title>
<desc lang="heb">פרק 345
העיתונאי חגי סגל מארח ומתווכח. כת' עב'
</desc>
</programme>
<programme start="20170313124500 +0200" stop="20170313132000 +0200" channel="41d97066c9c97e348f83269a6b18e8e6">
<title lang="heb">מעונן חלקית כת' עב' </title>
<sub-title lang="heb">פרק 286</sub-title>
<desc lang="heb">פרק 286
תחזית פוליטית: מבט אל השבוע הפוליטי והפרלמנטרי. מגישה: הדס לוי סצמסקי כת' עב'
</desc>
[ ... ]
</tv>
Note that the long hex blob marked red above matches the tvg-id entry of Channel 10 in the playlist given above. This allows pairing between an MPEG-TS stream and its info in the XMLTV file, and hence displaying the current TV program info for its respective channel.
Using Kodi as the front end
Kodi is a convenient front end for viewing TV on a media center computer, living room style. I suggest using the PVR IPTV Simple Client with a local file playlist, in particular because of the simplicity of this solution. And that it works so well.
The setup is fairly straightforward. First, install the plugin:
$ sudo apt-get install kodi-pvr-iptvsimple
and then, after having Kodi up and running, enable and set up the IPTV Simple Client as follows:
- Change setting level to Advanced
- System > Settings > Enable TV
- Enable and Configure PVR IPTV Simple Client (System > Settings > Add-ons > My add-ons > PVR Clients > PVR IPTV Simple Client). Set the playlist to local file, and pick one edited (as suggested above).
- Moving on to the EPG Settings tab, set Location to Remote Path, and XMLTV URL to http://localhost:9981/xmltv. As mentioned above, this requires a version of Tvheadend that supports XMLTV export. Check it manually with a browser.
The EPG interface isn’t necessary to watch TV properly, but makes Kodi display what’s on each channel in a neat way. As far as I know, the only alternative way to have EPG working with Kodi and Tvheadend is the Tvheadend Kodi plugin, which gave me errors all the time with the 4.09 version of Tvheadend.
Kodi has a screensaver enabled by default, which causes the screen to appear darker after a few minutes. It’s possible to turn it off under System > Settings > Appearance > Screensaver.
Summary
Use Kodi if you want it to look like a set-top-box, or vlc, ffplay or mplayer for a more computerish experience. Tvheadend gives a simple and robust interface to all of these, leaving the gory details to be forgotten. Once you’ve been through the setup, of course.
If Tvheadend doesn’t play ball, go for the command-line utilities.
And if all of this takes forever to complete, remember: TV is a waste of time either way.
Intro
These are my jots as I installed Linux Mint 18.1 on a Gigabyte GB-BACE-3160 Compact PC with a 240 GB SSD hard disk and 8 GB RAM, for the purpose of driving my TV in the living room. Not all issues are solved yet.
General notes
Installation flow
- BIOS is invoked by pressing the “Delete” button (constantly) during powerup
- Changed the OS in the BIOS to Linux (not clear why it matters)
- Always power on when AC power is applied: In the BIOS menu, Chipset > Restore AC Power Loss set to “Power on”.
- Pushed USB stick with Linux Mint 18.1 Cinnamon. Booted with no issues
- Installed ssh for remote access (ssh daemon starts immediately after installation)
# apt-get update
# apt-get install ssh
# passwd mint
- Set partition table manually: First partition for boot, 250 MB. Second partition for LVM, 120 GB. Leaving half the disk unpartitioned. LVM can easily use disk space from another partition in the future, if that’s needed. So it read in the end (fdisk):
Command (m for help): p
Disk /dev/sda: 223.6 GiB, 240057409536 bytes, 468862128 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x34449fd0
Device Boot Start End Sectors Size Id Type
/dev/sda1 2048 514047 512000 250M 83 Linux
/dev/sda2 514048 252172287 251658240 120G 8e Linux LVM
- Created one big logical volume on /dev/sda2:
# pvcreate /dev/sda2
Physical volume "/dev/sda2" successfully created
# vgcreate vg_ssd2 /dev/sda2
Volume group "vg_ssd2" successfully created
# lvcreate vg_ssd2 -l 100%FREE -n lv_root
Logical volume "lv_root" created.
- Launched the “Install Linux Mint”
- During installation, enabled installation of third party software (mp3 and media stuff). Installation type: “Something else” to pick what goes where. No swap partition. Installation went through cleanly
- Installed git (“apt-get install git”) and created a repository in root
- Installed the ssh daemon (apt-get install ssh)
- Stop the Linux splash. Give me some real boot text output, and this probably also solves some weird issues with the graphics card (mouse pointer traces and sluggish mouse response?):
Edit /etc/default/grub, changing GRUB_CMDLINE_LINUX_DEFAULT=”quiet splash” to GRUB_CMDLINE_LINUX_DEFAULT=”", and then update the actual GRUB2 file from the /etc/grub.d and the file just edited into /boot/grub/grub.cfg
# update-grub
- Also, make Grub’s menu always appear, since pressing Shift at bootup doesn’t work on this platform. So edit /etc/default/grub once again, uncommenting (explained in Ubuntu’s GRUB2 doc):
GRUB_HIDDEN_TIMEOUT=0
and setting
GRUB_TIMEOUT=2
GRUB_RECORDFAIL_TIMEOUT=2
and once again, run update-grub. The GRUB_RECORDFAIL_TIMEOUT part is required for all the times the computer is turned off abruptly (which is fine with an overlayfs). Otherwise GRUB sticks to 30 seconds all the time.
For more advanced GRUB trickery, see this post.
- DIDN’T: The kernel console boot output is given with some weird blocks instead of characters (The Matrix style) until the framebuffer module is initialized, and the screen goes into graphical mode, after which all looks OK. This can be fixed by uncommenting
#GRUB_TERMINAL=console
in /etc/default/grub, and running update-grub. The result is that these two seconds of kernel messages that are OK, but GRUB’s own menu is shown painfully slow. So I remain with the Matrix kernel.
- Ditch Apparmor, because it messes up things with overlayroot, and probably would cause all kind of weirdnesses regardless, exactly like SELinux did in the past (no chkconfig in Xenial).
$ sudo update-rc.d apparmor remove
- Fix /bin/sh symlink: By default, it goes to /bin/dash (someone had a bad sense of humor). Change it /bin/bash. Won’t make any difference in the matters of this post, but it will bite sometime in the long term.
- Add LSB (Linux Standard Base), among others for getting ld symlinks in /lib64/. Once again, won’t make any difference right now, but it should be there:
# apt-get install lsb-core
- Add group eli manually:
# addgroup --gid nnn eli
- And user:
# adduser --gid nnn --uid nnnn eli
Adding user `eli' ...
Adding new user `eli' (nnnn) with group `eli' ...
Creating home directory `/home/eli' ...
Copying files from `/etc/skel' ...
- Allow no-password access of all members of sudo group (only added NOPASSWD part). From /etc/sudoers:
%sudo ALL=(ALL:ALL) NOPASSWD: ALL
- Enable auto login: Pick the “Login Window” GUI application
- Assign a fixed address on DHCP daemon and a host name at /etc/hosts
- Install NFS client:
# apt-get install nfs-common
- Copy my own .bashrc stuff into “eli” and “root” users
- Install Xemacs:
# apt-get install xemacs21
- Allowing X-sessions over ssh from root user, following this post. As root,
# xauth merge /home/eli/.Xauthority
xauth: file /root/.Xauthority does not exist
# cd ~/
# ln -s /home/eli/.Xauthority
Note that I symlinked the file only because it didn’t exist before, and copying it makes the trick only until the next reboot.
- Set the power options (never turn off screen) and screensaver (ditto, it’s done separately)
- Install kodi, MythTV and mplayer (vlc was already installed):
$ sudo apt-get install --install-suggests kodi
$ sudo apt-get install --install-suggests mythtv
$ sudo apt-get install --install-suggests mplayer
- Install PVRs (mythtv and dvbviewer were probably redundant)
# apt-get install kodi-pvr-iptvsimple
# apt-get install --install-suggests kodi-pvr-dvbviewer
# apt-get install kodi-pvr-mythtv
# apt-get install kodi-pvr-hts
- Install DVB command-line utilities, which didn’t prove very useful (previously dvb-utils)
# apt-get install dvb-apps
- Install w_scan, useful for DVB channel scanning
# apt-get install w-scan
- After changing the resolution to 1920x1080 the menu panel at the bottom was too low down, making it almost invisible. This was the Samsung monitor’s fault, which needed to be adjusted to show the full screen.
- (No need to install kernel headers nor Make / compiler. All included out of the box)
- Install apt-file:
$ sudo apt-get install apt-file
$ apt-file update
- Installed intel-graphics-update-tool_2.0.2 (I messed up a bit with it, see below. Problem was that I attempted to install 2.0.3, which relates to Ubuntu 16.10, but Mint 18.1 is Ubuntu 16.04). It refused, as it looked at /etc/lsb-release, founding out that the distro is Mint. So I faked it.
$ sudo dpkg -i intel-graphics-update-tool_2.0.2_amd64.deb
$ sudo apt-get -f install
$ sudo intel-graphics-update-tool
- Some DVB command line tests (see separate post)
- Overlay root (see separate post)
- Turn off ureadahead service (for faster boot, but does it make sense on a SSD device?)
# systemctl disable ureadahead
- Install Tvheadend (see separate post)
- Add Kodi and Terminal as Startup Applications (using the desktop’s Menu > Startup Applications). Don’t. It’s important to see the desktop screen so the computer’s mode is clear.
Kodi setup
General note: Kodi remembers the navigation position in sub-menus when a category is entered. If things get too confusing, just restart Kodi, so the navigation paths in the docs match.
- Change setting level to Advanced
- System > Settings > Enable TV
- Enable and Configure PVR IPTV Simple Client with a local file playlist (System > Settings > Add-ons > My add-ons > PVR Clients > PVR IPTV Simple Client). Grab a playlist from http://localhost:9981/playlist and append hand-picked entries. In the EPG Settings tab, set Location to Remote Path, and XMLTV URL to http://localhost:9981/xmltv. This requires a recent (post-4.0.9) version of Tvheadend.
- On exit, use Ctrl-Alt-F1 and then Ctrl-Alt-F7 to get back from the blank screen it leaves (if necessary)
- Don’t install a TVheadend plugin. It got the original Tvheadend stuck, and the newer one requires picking a channel twice to view it. Besides, the Simple Client covers all needs with a single playlist.
Fixing graphics issues
Downloaded intel-graphics-update-tool_2.0.3_amd64.deb from Intel Graphics for Linux and ran
$ sudo dpkg -i intel-graphics-update-tool_2.0.3_amd64.deb
but that failed due to a dependency problem:
Selecting previously unselected package intel-graphics-update-tool.
(Reading database ... 207385 files and directories currently installed.)
Preparing to unpack intel-graphics-update-tool_2.0.3_amd64.deb ...
Unpacking intel-graphics-update-tool (2.0.3) ...
dpkg: dependency problems prevent configuration of intel-graphics-update-tool:
intel-graphics-update-tool depends on libpackagekit-glib2-18 (>= 0.9.4); however:
Package libpackagekit-glib2-18 is not installed.
intel-graphics-update-tool depends on fonts-ancient-scripts; however:
Package fonts-ancient-scripts is not installed.
dpkg: error processing package intel-graphics-update-tool (--install):
dependency problems - leaving unconfigured
Processing triggers for gnome-menus (3.13.3-6ubuntu3.1) ...
Processing triggers for desktop-file-utils (0.22-1ubuntu5) ...
Processing triggers for mime-support (3.59ubuntu1) ...
Errors were encountered while processing:
intel-graphics-update-tool
Rumor has it that apt-get -f install can fix that, but it said:
$ sudo apt-get -f install
Reading package lists... Done
Building dependency tree
Reading state information... Done
Correcting dependencies... Done
The following packages will be REMOVED:
intel-graphics-update-tool
0 upgraded, 0 newly installed, 1 to remove and 128 not upgraded.
Remove? Why? What have I done? How about this:
$ sudo apt-get install --fix-missing
Reading package lists... Done
Building dependency tree
Reading state information... Done
You might want to run 'apt-get -f install' to correct these.
The following packages have unmet dependencies:
intel-graphics-update-tool : Depends: libpackagekit-glib2-18 (>= 0.9.4) but it is not installable
Depends: fonts-ancient-scripts but it is not installed
So I ran apt-get -f install and dropped intel-graphics-update-tool.
Directories that change
In the event of restoring the entire root filesystem from backup, try to retain these:
- /.git (the main git repository)
- /home/eli (of course)
- /home/eli/.kodi (included in /home/eli, but this is where Kodi keeps its info)
- /home/hts/ (where Tvheadend keeps its settings and logs)
This is a lot of random jots as I set up Tvheadend. As it was a bit of a battle, things may not be consistent below. There are some details below specific to my own environment (e.g. LAN addresses).
Environment: Linux Mint 18.1 running a 4.4.0-53-generic kernel, receiving DVB-T in Haifa, Israel with a HD-901T2 DVB-T2 USB stick.
Installation
This is the backend server for accessing the DVB tuner. Any software that is used to view live TV expects some kind of backend like this.
Add TVheadend repository, following this guide: The following file added as /etc/apt/sources.list.d/tvheadend.list:
deb https://dl.bintray.com/tvheadend/deb xenial stable
and loaded the key:
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 379CE192D401AB61
Executing: /tmp/tmp.3BLJZuWTSs/gpg.1.sh --keyserver
hkp://keyserver.ubuntu.com:80
--recv-keys
379CE192D401AB61
gpg: requesting key D401AB61 from hkp server keyserver.ubuntu.com
gpg: key D401AB61: public key "Bintray (by JFrog) <bintray@bintray.com>" imported
gpg: Total number processed: 1
gpg: imported: 1 (RSA: 1)
and as usual,
# apt-get update
And install
# apt-get install tvheadend xmltv-util
ignoring the lack of verification (living on the edge):
WARNING: The following packages cannot be authenticated!
tvheadend
Install these packages without verification? [y/N] y
xmltv-util was added since it was suggested when requesting the installation of tvheadend.
As part of the installation of Tvheadend, I was prompted for a username a password for the dedicated tvheadend user. This is the user/password pair for accessing the web interface, and not the Linux user which is generated during the installation (named “hts” was created, with no password assigned, i.e. impossible to login with password).
And then configure the server by connecting to port 9981 with a plain web browser.
Following this guide:
- Went for the DVB Inputs tab, where /dev/dvb/adapter0 was found. Uhhh, there are three entries, two for Panasonic MN88473 (with DVB-C and DVB-T each) and an entry for Realtek RTL2832 (DVB-T). Picked the Realtek DVB-T #0 entry, as the Panasonic adapters can’t supply BER and SNR (and I killed it), as indicated in the debug output when this is attempted. Ticked the “enabled” checkbox. “Initial Scan” and “Idle scan” should be ticked as well. Click Save.
- Went to the Networks Tab, and created a new DVB-T network, named it Idan Plus. Check “Skip Initial scan” (it slows down startup). Set Pre-defined Muxes to Israel: il-All.
- Going back to the DVB Inputs tab, associate the DVB-T entry with the Network just created (drop-down menu for Networks). Click Save.
- Under “Muxes”, monitor the scan status on the 538 MHz mux (for Haifa) should go from PEND to IDLE and the result to “OK”. It takes a couple of minutes or so before anything happens.
- Disable the failed mux (not sure if it matters)
- Go to Services, and find a nice list of them.
- Map channels: On the Channel/EPG tab, click on the “Map Services” button, and accept the defaults.
- Add an access entry for kodi: Set the username to e.g. kodiclient, set a password. Allow all interfaces except admin, set the streaming profile to htsp, and check all functionalities (video recorder to All DVR-rw).
- Add an access entry for no-password users: Set the network prefix to “10.1.1.0/24;10.10.0.0/16;127.0.0.1/32″ with no credential request: Set the username to *, and the password to * (that is, a single “*” char each). Allow all interfaces, set the streaming profile to htsp, and check all functionalities (video recorder to All DVR-rw).
This is what a failed scan looks like (on tvheadend’s debug output):
2017-03-10 11:03:37.000 [ INFO] mpegts: 514MHz in Idan Plus - tuning on Realtek RTL2832 (DVB-T) : DVB-T #0
2017-03-10 11:03:37.000 [ INFO] opentv-ausat: registering mux 514MHz in Idan Plus
2017-03-10 11:03:37.004 [ INFO] subscription: 0002: "scan" subscribing to mux "514MHz", weight: 5, adapter: "Realtek RTL2832 (DVB-T) : DVB-T #0", network: "Idan Plus", service: "Raw PID Subscription"
2017-03-10 11:03:40.055 [WARNING] linuxdvb: Unhandled signal scale: 0
2017-03-10 11:03:40.069 [WARNING] linuxdvb: Unable to provide UNC value.
2017-03-10 11:03:42.000 [ INFO] mpegts: 514MHz in Idan Plus - scan no data, failed
2017-03-10 11:03:42.000 [ INFO] subscription: 0002: "scan" unsubscribing
2017-03-10 11:03:42.002 [ INFO] mpegts: 538MHz in Idan Plus - tuning on Realtek RTL2832 (DVB-T) : DVB-T #0
2017-03-10 11:03:42.002 [ INFO] opentv-ausat: registering mux 538MHz in Idan Plus
2017-03-10 11:03:42.009 [ INFO] subscription: 0004: "scan" subscribing to mux "538MHz", weight: 5, adapter: "Realtek RTL2832 (DVB-T) : DVB-T #0", network: "Idan Plus", service: "Raw PID Subscription"
2017-03-10 11:03:47.000 [ INFO] mpegts: 538MHz in Idan Plus - scan no data, failed
2017-03-10 11:03:47.000 [ INFO] subscription: 0004: "scan" unsubscribing
THIS IS THE SETTING FOR THE OLD Sin Hon TDH 601 / HD809 USB dongle. Just skip.
- Went for the DVB Inputs tab, where /dev/dvb/adapter0 was found. Picked the DVB-T #0 entry (there was a DVB-C entry as well, but this is for cable TV frequencies). Ticked the “enabled” checkbox. “Initial Scan” and “Idle scan” should be ticked as well. Click Save.
- Went to the Networks Tab, and created a new DVB-T network, named it Idan Plus. Uncheck “Skip Initial scan”. Set Pre-defined Muxes to Israel: il-All.
- Going back to the DVB Inputs tab, associate the DVB-T entry with the Network just created (drop-down menu for Networks). Click Save.
- Under “Muxes”, monitor the scan status on the 538 MHz mux (for Haifa) should go from PEND to IDLE and the result to “OK”. It takes a couple of minutes or so before anything happens.
- Disable the failed mux (not sure if it matters)
- Go to Services, and find a nice list of them.
- Map channels: On the Channel/EPG tab, click on the “Map Services” button, and accept the defaults.
- Add an access entry for kodi: Set the username to e.g. kodiclient, set a password. Allow all interfaces except admin, set the streaming profile to htsp, and check all functionalities (video recorder to All DVR-rw).
# service tvheadend stop
or, alternatively:
# systemctl stop tvheadend.service
(and start for turning it on). After stopping, running the service from console, as root:
# /usr/bin/tvheadend -f -u hts -g video
This launches a deamon (-f means “fork as daemon”), after changing the user ID and group as given by the flags.
To run it in the foreground, with debug messages to console,
# /usr/bin/tvheadend --trace --debug -d -u hts -g video
Which outputs during a successful scan of Idan Plus in Haifa:
2017-02-06 14:24:58.000 [ INFO] mpegts: 538MHz in Idan Plus - tuning on Silicon Labs Si2168 : DVB-T #0
2017-02-06 14:24:58.001 [ INFO] opentv-ausat: registering mux 538MHz in Idan Plus
2017-02-06 14:24:58.007 [ INFO] subscription: 0007: "scan" subscribing to mux "538MHz", weight: 5, adapter: "Silicon Labs Si2168 : DVB-T #0", network: "Idan Plus", service: "Raw PID Subscription"
2017-02-06 14:24:59.028 [WARNING] eit: invalid checksum (len 489, errors 1)
2017-02-06 14:24:59.698 [WARNING] mpegts: mux 514MHz in Idan Plus changed from DVBT freq 514000000 bw 8MHz cons QAM/16 hier NONE code_rate 2/3:NONE guard 1/4 trans 8k plp_id -1
2017-02-06 14:24:59.698 [WARNING] mpegts: mux 514MHz in Idan Plus changed to DVBT freq 514000000 bw 8MHz cons QAM/16 hier NONE code_rate 2/3:2/3 guard 1/4 trans 8k plp_id -1
2017-02-06 14:24:59.700 [WARNING] mpegts: mux 538MHz in Idan Plus changed from DVBT freq 538000000 bw 8MHz cons QAM/16 hier NONE code_rate 2/3:NONE guard 1/4 trans 8k plp_id -1
2017-02-06 14:24:59.700 [WARNING] mpegts: mux 538MHz in Idan Plus changed to DVBT freq 538000000 bw 8MHz cons QAM/16 hier NONE code_rate 2/3:2/3 guard 1/4 trans 8k plp_id -1
2017-02-06 14:25:03.000 [ INFO] mpegts: 538MHz in Idan Plus - scan complete
2017-02-06 14:25:03.000 [ INFO] subscription: 0007: "scan" unsubscribing
2017-02-06 14:25:03.004 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0027/39] (missing in PAT/SDT)
2017-02-06 14:25:03.004 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0026/38] (missing in PAT/SDT)
2017-02-06 14:25:03.004 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0025/37] (missing in PAT/SDT)
2017-02-06 14:25:03.004 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0023/35] (missing in PAT/SDT)
2017-02-06 14:25:03.004 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0022/34] (missing in PAT/SDT)
2017-02-06 14:25:03.004 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0021/33] (missing in PAT/SDT)
2017-02-06 14:25:03.004 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0020/32] (missing in PAT/SDT)
2017-02-06 14:25:03.004 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 001F/31] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0014/20] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0013/19] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0012/18] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0011/17] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 0010/16] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 000F/15] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 000E/14] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 000D/13] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 000C/12] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 000B/11] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: disabling service Idan Plus/538MHz/{PMT:0} [sid 000A/10] (missing in PAT/SDT)
2017-02-06 14:25:03.005 [ INFO] mpegts: 514MHz in Idan Plus - tuning on Silicon Labs Si2168 : DVB-T #0
2017-02-06 14:25:03.005 [ INFO] opentv-ausat: registering mux 514MHz in Idan Plus
2017-02-06 14:25:03.012 [ INFO] subscription: 0009: "scan" subscribing to mux "514MHz", weight: 5, adapter: "Silicon Labs Si2168 : DVB-T #0", network: "Idan Plus", service: "Raw PID Subscription"
2017-02-06 14:25:08.000 [ INFO] mpegts: 514MHz in Idan Plus - scan no data, failed
2017-02-06 14:25:08.000 [ INFO] subscription: 0009: "scan" unsubscribing
An .m3u8 playlist can be obtained with http://localhost:9981/playlist (or was it http://localhost:9981/playlist/channels?). Anyhow, if Tvheadend doesn’t like the URL, it goes “1 Unknown code”).
The playlist can be used with e.g. VLC connect to to play channels, possible several different channels from the same DVB adapter, if they share the same broadcast frequency ( = same mux in Tvheadend terminology).
Where tvheadend keeps its configuration
Short answer: /home/hts/.hts/tvheadend/. Don’t get scared by all those SHA1-named files. It’s not a git repo. These are plain, readable, text files which are easy to understand.
In particular, the DVR timers are stored in /home/hts/.hts/tvheadend/dvr/timerec/, one file for each timer task. Really simple. The file names don’t change when the configuration changes, so I guess these files can be tracked with version control.
Source digging
Grabbing the repository:
$ git clone https://github.com/tvheadend/tvheadend
Using “git blame” on src/webui/webui.c, it’s evident that Jaroslav Kysela added the /xmltv feature on Oct. 28th 2015 (as he said himself), and there are functional changes running as far as commit ID a87689803093e303ac4d458d35ac92c1bfc41f96 of Nov 11th 2015, which does XML escaping.
This post shows some JSON API as well.
Upgrade tvheadend
Since Kodi’s HTSP Tvheadend client worked horribly, I went for IPTV simple client from one of those IPTV providers in the UK. But in order to get the EPG feature working, I need a Tvheadend version that exports an XMLTV file through http. Ironically, the upgrade improved things with the HTSP client significantly, but there was still a need to double-click a channel twice to zap.
Change /etc/apt/sources.list.d/tvheadend.list to
deb https://dl.bintray.com/tvheadend/deb xenial unstable
and go
# apt-get update
# apt-get install tvheadend
Ignore warning that the package can’t be authenticated, and go ahead installing (upgrading, in my case). The new revision was 4.1-2405~geb495a0~xenial, according to “About” in the Web UI, which is git commit eb495a0984245c37a4474c7d3984acdd34edc071 from Jan 2nd, 2017. So not the absolutely latest, but definitely covers the /xmltv feature.
Files that were updated: /usr/bin/tvheadend, files under /usr/share/doc/tvheadend and a lot under /usr/share/tvheadend, and the man page (plus minus, this is based upon files an overlay filesystem).
Alternatively, this is the manual installation of the same. Look for tvheadend_4.1-2405~geb495a0~xenial_amd64.deb (it’s on my hard disk as well, under /storage/tv-mediacenter-stuff). Remove old version and install new:
# apt-get remove tvheadend
# dpkg -i tvheadend_4.1-2405~geb495a0~xenial_amd64.deb
This revision works great.
Some useful access URLs
Assuming that the server is on localhost:
- Obtain m3U playlist of all live TV channels: http://localhost:9981/playlist
- Obtain XMLTV file for EPG: http://localhost:9981/xmltv (or better, http://localhost:9981/xmltv/channels)
- Watch TV channels directly from a browser (rather crude look, but works): http://localhost:9981/tv.html
- Recorder log: http://localhost:9981/simple.html
And as mentioned above, there as several URLs for JSON formatted info on this post. I suppose I’ll add more as I come across these…
This is the information obtained with xrandr from the HDMI to AV converter (Composite Video + Audio on RCA plugs) shown above:
Screen 0: minimum 8 x 8, current 1280 x 720, maximum 32767 x 32767
DP1 disconnected (normal left inverted right x axis y axis)
DP2 disconnected (normal left inverted right x axis y axis)
DP3 disconnected (normal left inverted right x axis y axis)
HDMI1 disconnected (normal left inverted right x axis y axis)
HDMI2 disconnected (normal left inverted right x axis y axis)
HDMI3 connected primary 1280x720+0+0 (normal left inverted right x axis y axis) 708mm x 398mm
1280x720 60.00*+ 59.94
1920x1080 60.00 59.94
1600x1200 60.00
1680x1050 59.88
1400x1050 59.95
1600x900 60.00
1280x1024 60.02
1440x900 59.90
1280x960 60.00
1280x800 59.91
1024x768 60.00
800x600 60.32
720x480 60.00 59.94
640x480 60.00 59.94
VIRTUAL1 disconnected (normal left inverted right x axis y axis)
And with “verbose” flag:
Screen 0: minimum 8 x 8, current 1280 x 720, maximum 32767 x 32767
DP1 disconnected (normal left inverted right x axis y axis)
Identifier: 0x43
Timestamp: 919743
Subpixel: unknown
Clones:
CRTCs: 1 2
Transform: 1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
filter:
Broadcast RGB: Automatic
supported: Automatic, Full, Limited 16:235
audio: auto
supported: force-dvi, off, auto, on
DP2 disconnected (normal left inverted right x axis y axis)
Identifier: 0x44
Timestamp: 919743
Subpixel: unknown
Clones:
CRTCs: 1 2
Transform: 1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
filter:
Broadcast RGB: Automatic
supported: Automatic, Full, Limited 16:235
audio: auto
supported: force-dvi, off, auto, on
DP3 disconnected (normal left inverted right x axis y axis)
Identifier: 0x45
Timestamp: 919743
Subpixel: unknown
Clones:
CRTCs: 0
Transform: 1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
filter:
Broadcast RGB: Automatic
supported: Automatic, Full, Limited 16:235
audio: auto
supported: force-dvi, off, auto, on
HDMI1 disconnected (normal left inverted right x axis y axis)
Identifier: 0x46
Timestamp: 919743
Subpixel: unknown
Clones:
CRTCs: 1 2
Transform: 1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
filter:
aspect ratio: Automatic
supported: Automatic, 4:3, 16:9
Broadcast RGB: Automatic
supported: Automatic, Full, Limited 16:235
audio: auto
supported: force-dvi, off, auto, on
HDMI2 disconnected (normal left inverted right x axis y axis)
Identifier: 0x47
Timestamp: 919743
Subpixel: unknown
Clones:
CRTCs: 1 2
Transform: 1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
filter:
aspect ratio: Automatic
supported: Automatic, 4:3, 16:9
Broadcast RGB: Automatic
supported: Automatic, Full, Limited 16:235
audio: auto
supported: force-dvi, off, auto, on
HDMI3 connected primary 1280x720+0+0 (0x11d) normal (normal left inverted right x axis y axis) 708mm x 398mm
Identifier: 0x48
Timestamp: 919743
Subpixel: unknown
Gamma: 1.0:1.0:1.0
Brightness: 1.0
Clones:
CRTC: 0
CRTCs: 0
Transform: 1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
filter:
EDID:
00ffffffffffff00328d000000000000
2d170103e87341782aee91a3544c9926
0f505421080081008140818090409500
a9c0b300a940011d007251d01e206e28
5500c48e2100001ef339801871382d40
582c4500c48e2100001e000000fd001e
551e6414000a202020202020000000fc
0048444d493241560a202020002001d6
02031841458403020110230907078301
000065030c001000011d007251d01e20
6e28550010090000001e011d007251d0
1e206e28550010090000001e8c0ad08a
20e02d10103e96001009000000180000
00000000000000000000000000000000
00000000000000000000000000000000
0000000000000000000000000000001d
aspect ratio: Automatic
supported: Automatic, 4:3, 16:9
Broadcast RGB: Automatic
supported: Automatic, Full, Limited 16:235
audio: auto
supported: force-dvi, off, auto, on
1280x720 (0x11d) 74.250MHz +HSync +VSync *current +preferred
h: width 1280 start 1390 end 1430 total 1650 skew 0 clock 45.00KHz
v: height 720 start 725 end 730 total 750 clock 60.00Hz
1920x1080 (0x4b) 148.500MHz +HSync +VSync
h: width 1920 start 2008 end 2052 total 2200 skew 0 clock 67.50KHz
v: height 1080 start 1084 end 1089 total 1125 clock 60.00Hz
1920x1080 (0x10b) 148.352MHz +HSync +VSync
h: width 1920 start 2008 end 2052 total 2200 skew 0 clock 67.43KHz
v: height 1080 start 1084 end 1089 total 1125 clock 59.94Hz
1600x1200 (0x114) 162.000MHz +HSync +VSync
h: width 1600 start 1664 end 1856 total 2160 skew 0 clock 75.00KHz
v: height 1200 start 1201 end 1204 total 1250 clock 60.00Hz
1680x1050 (0x115) 119.000MHz +HSync -VSync
h: width 1680 start 1728 end 1760 total 1840 skew 0 clock 64.67KHz
v: height 1050 start 1053 end 1059 total 1080 clock 59.88Hz
1400x1050 (0x13b) 101.000MHz +HSync -VSync
h: width 1400 start 1448 end 1480 total 1560 skew 0 clock 64.74KHz
v: height 1050 start 1053 end 1057 total 1080 clock 59.95Hz
1600x900 (0x13c) 108.000MHz +HSync +VSync
h: width 1600 start 1624 end 1704 total 1800 skew 0 clock 60.00KHz
v: height 900 start 901 end 904 total 1000 clock 60.00Hz
1280x1024 (0x117) 108.000MHz +HSync +VSync
h: width 1280 start 1328 end 1440 total 1688 skew 0 clock 63.98KHz
v: height 1024 start 1025 end 1028 total 1066 clock 60.02Hz
1440x900 (0x119) 88.750MHz +HSync -VSync
h: width 1440 start 1488 end 1520 total 1600 skew 0 clock 55.47KHz
v: height 900 start 903 end 909 total 926 clock 59.90Hz
1280x960 (0x11a) 108.000MHz +HSync +VSync
h: width 1280 start 1376 end 1488 total 1800 skew 0 clock 60.00KHz
v: height 960 start 961 end 964 total 1000 clock 60.00Hz
1280x800 (0x11b) 71.000MHz +HSync -VSync
h: width 1280 start 1328 end 1360 total 1440 skew 0 clock 49.31KHz
v: height 800 start 803 end 809 total 823 clock 59.91Hz
1280x720 (0x11f) 74.176MHz +HSync +VSync
h: width 1280 start 1390 end 1430 total 1650 skew 0 clock 44.96KHz
v: height 720 start 725 end 730 total 750 clock 59.94Hz
1024x768 (0x122) 65.000MHz -HSync -VSync
h: width 1024 start 1048 end 1184 total 1344 skew 0 clock 48.36KHz
v: height 768 start 771 end 777 total 806 clock 60.00Hz
800x600 (0x126) 40.000MHz +HSync +VSync
h: width 800 start 840 end 968 total 1056 skew 0 clock 37.88KHz
v: height 600 start 601 end 605 total 628 clock 60.32Hz
720x480 (0x129) 27.027MHz -HSync -VSync
h: width 720 start 736 end 798 total 858 skew 0 clock 31.50KHz
v: height 480 start 489 end 495 total 525 clock 60.00Hz
720x480 (0x12a) 27.000MHz -HSync -VSync
h: width 720 start 736 end 798 total 858 skew 0 clock 31.47KHz
v: height 480 start 489 end 495 total 525 clock 59.94Hz
640x480 (0x12e) 25.200MHz -HSync -VSync
h: width 640 start 656 end 752 total 800 skew 0 clock 31.50KHz
v: height 480 start 490 end 492 total 525 clock 60.00Hz
640x480 (0x12f) 25.175MHz -HSync -VSync
h: width 640 start 656 end 752 total 800 skew 0 clock 31.47KHz
v: height 480 start 490 end 492 total 525 clock 59.94Hz
VIRTUAL1 disconnected (normal left inverted right x axis y axis)
Identifier: 0x49
Timestamp: 919743
Subpixel: no subpixels
Clones:
CRTCs: 3
Transform: 1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000
filter:
This was done with the switch in “PAL” position, but I’m not sure it matters.
The problem
Having A HD-901T2 DVB-T2 stick on a Linux Mint 18.1 running a 4.4.0-53-generic kernel, Tvheadend started painfully slow. It took more than a minute before the web interface was available. And here’s why:
Mar 11 10:22:35 tv systemd[1]: Starting tvheadend.service...
Mar 11 10:22:35 tv tvheadend[6019]: * Starting Tvheadend tvheadend
Mar 11 10:22:35 tv tvheadend[6019]: ...done.
Mar 11 10:22:35 tv systemd[1]: Started tvheadend.service.
Mar 11 10:22:35 tv tvheadend[6030]: main: Log started
Mar 11 10:22:35 tv tvheadend[6030]: config: loaded
Mar 11 10:22:35 tv tvheadend[6030]: scanfile: DVB-T - loaded 43 regions with 1106 networks
Mar 11 10:22:35 tv tvheadend[6030]: scanfile: DVB-S - loaded 1 regions with 112 networks
Mar 11 10:22:35 tv tvheadend[6030]: scanfile: DVB-C - loaded 17 regions with 56 networks
Mar 11 10:22:35 tv tvheadend[6030]: scanfile: ATSC - loaded 2 regions with 14 networks
Mar 11 10:22:40 tv tvheadend[6030]: linuxdvb: adapter added /dev/dvb/adapter0
Mar 11 10:22:40 tv kernel: [ 5333.042417] usb 1-4: DVB: adapter 0 frontend 0 frequency 0 out of range (174000000..862000000)
Mar 11 10:24:23 tv tvheadend[6030]: linuxdvb: unable to open /dev/dvb/adapter0/frontend1
So that’s it: Tvheadend fails with /dev/dvb/adapter0/frontend1 somehow, and that failure takes time (the driver’s access timeouts?) and blocks the setup of other functionalities. The thing is that frontend1 is in fact the front end for the Panasonic MN88473 DVB-T/T2/C demodulator, and is redundant, because Tvheadend’s interface with the DVB stick is through /dev/dvb/adapter0/frontend0 (Realtek RTL2832).
The elegant solution is to make the offending device file inaccessible using udev. Since udev doesn’t rename device files that are created by the kernel for a long time, playing with its permissions is the way out. If Tvheadend is denied access to /dev/dvb/adapter0/frontend1, it won’t mess with it.
I have old post on udev hacking, and one on udev rules for a NIC, by the way.
You may skip the following section, and go directly to “The fix”, if the gory details seem boring.
Analyzing the udev situation
Plugging in the DVB stick into the computer with
# udevadm monitor --udev --property
running makes a lot of output, but this among others:
UDEV [90.760633] add /devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1 (dvb)
ACTION=add
DEVNAME=/dev/dvb/adapter0/frontend1
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1
DVB_ADAPTER_NUM=0
DVB_DEVICE_NUM=1
DVB_DEVICE_TYPE=frontend
ID_FOR_SEAT=dvb-pci-0000_00_14_0-usb-0_4
ID_PATH=pci-0000:00:14.0-usb-0:4
ID_PATH_TAG=pci-0000_00_14_0-usb-0_4
MAJOR=212
MINOR=4
SEQNUM=2829
SUBSYSTEM=dvb
TAGS=:uaccess:seat:
USEC_INITIALIZED=90759736
And looking at the device itself:
$ udevadm info -a -n /dev/dvb/adapter0/frontend1
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-3/dvb/dvb0.frontend1':
KERNEL=="dvb0.frontend1"
SUBSYSTEM=="dvb"
DRIVER==""
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-3':
KERNELS=="1-3"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bMaxPower}=="500mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 2"
ATTRS{bcdDevice}=="0100"
ATTRS{bmAttributes}=="80"
ATTRS{busnum}=="1"
ATTRS{configuration}=="USB2.0-Bulk&Iso"
ATTRS{devnum}=="3"
ATTRS{devpath}=="3"
ATTRS{idProduct}=="0131"
ATTRS{idVendor}=="15f4"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="astrometadvbt2"
ATTRS{maxchild}=="0"
ATTRS{product}=="dvbt2"
ATTRS{quirks}=="0x0"
ATTRS{removable}=="removable"
ATTRS{speed}=="480"
ATTRS{urbnum}=="18880"
ATTRS{version}==" 2.00"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1':
KERNELS=="usb1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{authorized_default}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="09"
ATTRS{bDeviceProtocol}=="01"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bMaxPower}=="0mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bcdDevice}=="0404"
ATTRS{bmAttributes}=="e0"
ATTRS{busnum}=="1"
ATTRS{configuration}==""
ATTRS{devnum}=="1"
ATTRS{devpath}=="0"
ATTRS{idProduct}=="0002"
ATTRS{idVendor}=="1d6b"
ATTRS{interface_authorized_default}=="1"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="Linux 4.4.0-53-generic xhci-hcd"
ATTRS{maxchild}=="7"
ATTRS{product}=="xHCI Host Controller"
ATTRS{quirks}=="0x0"
ATTRS{removable}=="unknown"
ATTRS{serial}=="0000:00:14.0"
ATTRS{speed}=="480"
ATTRS{urbnum}=="52"
ATTRS{version}==" 2.00"
looking at parent device '/devices/pci0000:00/0000:00:14.0':
KERNELS=="0000:00:14.0"
SUBSYSTEMS=="pci"
DRIVERS=="xhci_hcd"
ATTRS{broken_parity_status}=="0"
ATTRS{class}=="0x0c0330"
ATTRS{consistent_dma_mask_bits}=="64"
ATTRS{d3cold_allowed}=="1"
ATTRS{device}=="0x22b5"
ATTRS{dma_mask_bits}=="64"
ATTRS{driver_override}=="(null)"
ATTRS{enable}=="1"
ATTRS{irq}=="115"
ATTRS{local_cpulist}=="0-3"
ATTRS{local_cpus}=="f"
ATTRS{msi_bus}=="1"
ATTRS{numa_node}=="-1"
ATTRS{subsystem_device}=="0x1000"
ATTRS{subsystem_vendor}=="0x1458"
ATTRS{vendor}=="0x8086"
looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""
Only the first two entries are interesting: The others climb up through the USB port, the PCIebus and finally the PCI root port.
The fix
So I set up this file as /etc/udev/rules.d/50-dvb-rename.rules
SUBSYSTEM=="dvb", ATTRS{idVendor}=="15f4", ATTRS{idProduct}=="0131", KERNEL=="dvb*.frontend1", MODE:="0000", GROUP:="root"
And refreshed the udev system:
# udevadm control --reload
Don’t get discouraged by /etc/udev/ being almost empty: The systemd rules have moved to /lib/udev/, and following systemd conventions, local fixes should be in /etc/udev/.
That didn’t work exactly as I expected, but good enough (after replugging the DVB stick):
$ ls -l /dev/dvb/adapter0/
total 0
crw-rw----+ 1 root video 212, 0 Mar 11 10:50 demux0
crw-rw----+ 1 root video 212, 1 Mar 11 10:50 dvr0
crw-rw----+ 1 root video 212, 3 Mar 11 10:50 frontend0
c---rw----+ 1 root root 212, 4 Mar 11 10:50 frontend1
crw-rw----+ 1 root video 212, 2 Mar 11 10:50 net0
WTF? Note that the MODE assignment is with a := which is supposed to prevent overruling. And still, it’s group accessible. Where did that come from? Anyhow, that’s why the group is set to root. That fixed the problem with Tvheadend, which now finishes initializing 12 seconds after systemd announces its launch.
More udev rambling
I went on a bit on this. Nothing really for TLDRers.
Tesring (doesn’t really require root privileges, but the actions won’t take place):
# udevadm test -a add $(udevadm info -q path -n /dev/dvb/adapter0/frontend1)
or stating the path explicitly:
# udevadm info -q path -n /dev/dvb/adapter0/frontend1
/devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1
# udevadm test -a add /devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1
[ ... ]
GROUP 0 /etc/udev/rules.d/50-dvb-rename.rules:4
MODE 0 /etc/udev/rules.d/50-dvb-rename.rules:4
IMPORT builtin 'path_id' /lib/udev/rules.d/71-seat.rules:49
RUN 'uaccess' /lib/udev/rules.d/73-seat-late.rules:15
handling device node '/dev/dvb/adapter0/frontend1', devnum=c212:4, mode=0, uid=0
, gid=0
set permissions /dev/dvb/adapter0/frontend1, 020000, uid=0, gid=0
preserve already existing symlink '/dev/char/212:4' to '../dvb/adapter0/frontend
1'
created db file '/run/udev/data/c212:4' for '/devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1'
[ ... ]
And here comes the big surprise: After the test, the frontend1′s permission was actually 0000 as required! So who’s fiddling with the permission after udev?
$ ls -l /dev/dvb/adapter0/total 0
crw-rw----+ 1 root video 212, 0 Mar 11 10:50 demux0
crw-rw----+ 1 root video 212, 1 Mar 11 10:50 dvr0
crw-rw----+ 1 root video 212, 3 Mar 11 10:50 frontend0
c---------+ 1 root root 212, 4 Mar 11 10:53 frontend1
crw-rw----+ 1 root video 212, 2 Mar 11 10:50 net0
If anyone knows, please comment below. For my current purpose, I could work this around by changing the group.
And if I’m at it: When Tvheadend runs, all device files in /dev/dvb/adapter0/ appear to have been deleted. They return when the Tvheadend is stopped. Does anyone have an idea on how and why?
Unlike what one might have expected, there was no need to update the initramfs. The attributes of frontend1 were the same (as the replug above, not the test) after a reboot:
# mount -o remount,rw /boot/
# update-initramfs -u -v
which didn’t make any difference, as expected.
Some (or all?) DVB adapters can be set to submit the entire MPEG-TS stream to the host, without filtering specific channels (actually, PIDs). This allows viewing more channel at a time. This is demonstrated below with ffplay, even though ffplay tends to get stuck when it goes to a live stream.
It’s also not possible to view multiple channels with ffplay like this, because the device file can be opened only by a single instance of ffplay (or there’s a device or resource busy error). So in short, this is really just a proof of concept. Tvheadend does this anyhow. But the point there is to do it low-level style.
What I had: A HD-901T2 DVB-T2 stick on a Linux Mint 18.1 running a 4.4.0-53-generic kernel.
First, a scan should be done with dvbv5-scan, which creates a dvb_channel.conf file (shown in this post)
Using the -P flag to disable channel filtering:
$ dvbv5-zap -P -c dvb_channel.conf -r 'Ch 10'
using demux '/dev/dvb/adapter0/demux0'
reading channels from file 'dvb_channel.conf'
service has pid type 06: 2642 2641 2640
tuning to 538000000 Hz
pass all PID's to TS
dvb_set_pesfilter 8192
(0x00)
Lock (0x1f)
Lock (0x1f)
DVR interface '/dev/dvb/adapter0/dvr0' can now be opened
Lock (0x1f) Signal= 92.55% C/N= 33.62dB postBER= 0
Note that the specification of ‘Ch 10′ in the command was merely a lazy way to set the frequency and other technical parameters.
Without stopping dvbv5-zap, go
$ ffplay /dev/dvb/adapter0/dvr0 2>log
and quit as soon as an image window appears. It’s a lot of mumbo-jumbo, but there’s a stream list somewhere in the middle — the example I captured is given at the end of this post.
So, to watch Channel 2:
$ ffplay -vst p:2 /dev/dvb/adapter0/dvr0
The p:2 specifier matches the “Program 2″ in the stream list. ffplay automatically matches the audio channel (even though -ast wasn’t specified).
Note that it’s ffplay that does the demuxing. This works (even better, actually) if the data stream is copied into a file first (with cat, for example) and then the file is played.
It also possible to handpick the streams with the PIDs, as listed in the dvb_channel.conf file (or in the stream list, as hex numbers in the square brackets). For example, watching channel 99:
$ ffplay -vst i:2689 -ast i:2690 /dev/dvb/adapter0/dvr0
or, equivalently
$ ffplay -vst i:0xa81 -ast i:0xa82 /dev/dvb/adapter0/dvr0
ffplay stream list
As captured in Haifa, Israel on March 10th 2017:
Program 1
Metadata:
service_name : ?Ch 1
service_provider: ?Idan +
Stream #0:30[0x401](heb): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:12[0xa01]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt470bg), 720x576 [SAR 12:11 DAR 15:11], 25 fps, 50 tbr, 90k tbn, 50 tbc
Stream #0:31[0xa02]: Audio: aac_latm (HE-AACv2) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 2
Metadata:
service_name : ?Ch 2
service_provider: ?Idan +
Stream #0:0[0xa21]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt470bg), 720x576 [SAR 12:11 DAR 15:11], 25 fps, 50 tbr, 90k tbn, 50 tbc
Stream #0:1[0xa22]: Audio: aac_latm (HE-AACv2) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Stream #0:2[0xa23]: Audio: aac_latm (HE-AACv2) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Stream #0:3[0xa30](heb): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:4[0xa31](rus): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:5[0xa32](ara): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Program 3
Metadata:
service_name : ?Ch 10
service_provider: ?Idan +
Stream #0:6[0xa41]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt470bg), 720x576 [SAR 12:11 DAR 15:11], 25 fps, 50 tbr, 90k tbn, 50 tbc
Stream #0:29[0xa42]: Audio: aac_latm (HE-AACv2) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Stream #0:28[0xa43]: Audio: aac_latm (HE-AACv2) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Stream #0:32[0xa50](heb): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:33[0xa51](rus): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:34[0xa52](ara): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Program 4
Metadata:
service_name : ?Ch 33
service_provider: ?Idan +
Stream #0:22[0xa61]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt470bg), 720x576 [SAR 12:11 DAR 15:11], 25 fps, 50 tbr, 90k tbn, 50 tbc
Stream #0:23[0xa62]: Audio: aac_latm (HE-AACv2) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 5
Metadata:
service_name : ?Ch 99
service_provider: ?Idan +
Stream #0:11[0xa81]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt470bg), 720x576 [SAR 12:11 DAR 15:11], 25 fps, 50 tbr, 90k tbn, 50 tbc
Stream #0:26[0xa82]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Stream #0:35[0xa90](heb): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Program 6
Metadata:
service_name : ?Ch 23
service_provider: ?Idan +
Stream #0:8[0xe01]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt470bg), 720x576 [SAR 12:11 DAR 15:11], 25 fps, 50 tbr, 90k tbn, 50 tbc
Stream #0:27[0xe02]: Audio: aac_latm (HE-AACv2) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 21
Metadata:
service_name : Aleph
service_provider: Idan +
Stream #0:19[0xb01]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 22
Metadata:
service_name : Bet
service_provider: Idan +
Stream #0:18[0xb11]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 23
Metadata:
service_name : Gimmel
service_provider: Idan +
Stream #0:7[0xb21]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 24
Metadata:
service_name : Dalet
service_provider: Idan +
Stream #0:9[0xb31]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 25
Metadata:
service_name : Moreshet
service_provider: Idan +
Stream #0:25[0xb41]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 26
Metadata:
service_name : 88FM
service_provider: Idan +
Stream #0:13[0xb51]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 27
Metadata:
service_name : Musica
service_provider: Idan +
Stream #0:16[0xb61]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 28
Metadata:
service_name : Reka
service_provider: Idan +
Stream #0:20[0xb71]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 29
Metadata:
service_name : Galatz
service_provider: Idan +
Stream #0:24[0xb81]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 30
Metadata:
service_name : Galgalatz
service_provider: Idan +
Stream #0:14[0xb91]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 36
Metadata:
service_name : Radios
service_provider: Idan +
Stream #0:15[0xbf1]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 41
Metadata:
service_name : Kol Barama
service_provider: Idan +
Stream #0:21[0xc41]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 42
Metadata:
service_name : Lev HaMdina
service_provider: Idan +
Stream #0:17[0xc51]: Audio: aac_latm (HE-AACv2) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 45
Metadata:
service_name : CLASSICAL bu
service_provider: Idan +
Stream #0:10[0xc81]: Audio: aac_latm (HE-AAC) ([17][0][0][0] / 0x0011), 48000 Hz, stereo, fltp
Program 7
Metadata:
service_name : Idan+test_2
service_provider: Idan+
So this list leaves no doubt that all channels were passed on to ffplay.