SURF  1.0
UdpEngineDhcp.vhd
Go to the documentation of this file.
1 -------------------------------------------------------------------------------
2 -- File : UdpEngineDhcp.vhd
3 -- Company : SLAC National Accelerator Laboratory
4 -- Created : 2016-08-12
5 -- Last update: 2017-05-10
6 -------------------------------------------------------------------------------
7 -- Description: DHCP Engine
8 -------------------------------------------------------------------------------
9 -- This file is part of 'SLAC Firmware Standard Library'.
10 -- It is subject to the license terms in the LICENSE.txt file found in the
11 -- top-level directory of this distribution and at:
12 -- https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html.
13 -- No part of 'SLAC Firmware Standard Library', including this file,
14 -- may be copied, modified, propagated, or distributed except according to
15 -- the terms contained in the LICENSE.txt file.
16 -------------------------------------------------------------------------------
17 
18 library ieee;
19 use ieee.std_logic_1164.all;
20 use ieee.std_logic_unsigned.all;
21 use ieee.std_logic_arith.all;
22 
23 use work.StdRtlPkg.all;
24 use work.AxiStreamPkg.all;
25 use work.SsiPkg.all;
26 use work.EthMacPkg.all;
27 
28 --! @see entity
29  --! @ingroup ethernet_UdpEngine
30 entity UdpEngineDhcp is
31  generic (
32  -- Simulation Generics
33  TPD_G : time := 1 ns;
34  -- UDP ARP/DHCP Generics
35  CLK_FREQ_G : real := 156.25E+06; -- In units of Hz
36  COMM_TIMEOUT_G : positive := 30);
37  port (
38  -- Local Configurations
39  localMac : in slv(47 downto 0); -- big-Endian configuration
40  localIp : in slv(31 downto 0); -- big-Endian configuration
41  dhcpIp : out slv(31 downto 0); -- big-Endian configuration
42  -- Interface to DHCP Engine
47  -- Clock and Reset
48  clk : in sl;
49  rst : in sl);
50 end UdpEngineDhcp;
51 
52 architecture rtl of UdpEngineDhcp is
53 
54  constant DHCP_CONFIG_C : AxiStreamConfigType := ssiAxiStreamConfig(4);
55  constant TIMER_1_SEC_C : natural := getTimeRatio(CLK_FREQ_G, 1.0);
56  constant CLIENT_HDR_C : slv(31 downto 0) := x"00060101"; -- 0x01010600
57  constant SERVER_HDR_C : slv(31 downto 0) := x"00060102"; -- 0x02010600
58  constant MAGIC_COOKIE_C : slv(31 downto 0) := x"63538263"; -- 0x63825363
59  constant COMM_TIMEOUT_C : positive := ite((COMM_TIMEOUT_G > 3), COMM_TIMEOUT_G, 3);
60 
61  type StateType is (
62  IDLE_S,
63  REQ_S,
64  BOOTP_S,
65  DHCP_S,
66  VERIFY_S);
67 
68  type DecodeType is (
69  CODE_S,
70  LEN_S,
71  DATA_S);
72 
73  type RegType is record
74  heartbeat : sl;
75  cnt : natural range 0 to 127;
76  timer : natural range 0 to (TIMER_1_SEC_C-1);
77  commCnt : natural range 0 to COMM_TIMEOUT_C;
78  renewCnt : slv(30 downto 0);
79  leaseCnt : slv(31 downto 0);
80  leaseTime : slv(31 downto 0);
81  remoteMac : slv(47 downto 0);
82  remoteIp : slv(31 downto 0);
83  dhcpIp : slv(31 downto 0);
84  dhcpReq : sl;
85  xid : slv(31 downto 0);
86  yiaddr : slv(31 downto 0);
87  siaddr : slv(31 downto 0);
88  yiaddrTemp : slv(31 downto 0);
89  siaddrTemp : slv(31 downto 0);
90  index : natural range 0 to 3;
91  valid : slv(4 downto 0);
92  opCode : slv(7 downto 0);
93  len : slv(7 downto 0);
94  msgType : slv(7 downto 0);
95  rxSlave : AxiStreamSlaveType;
96  txMaster : AxiStreamMasterType;
97  decode : DecodeType;
98  state : StateType;
99  end record RegType;
100  constant REG_INIT_C : RegType := (
101  heartbeat => '0',
102  cnt => 0,
103  timer => 0,
104  commCnt => 3, -- Default to 3 seconds after bootup
105  renewCnt => (others => '0'),
106  leaseCnt => (others => '0'),
107  leaseTime => (others => '0'),
108  remoteMac => (others => '1'), -- Broadcast MAC Address
109  remoteIp => (others => '1'), -- Broadcast IP Address
110  dhcpIP => (others => '0'),
111  dhcpReq => '0',
112  xid => (others => '0'),
113  yiaddr => (others => '0'),
114  siaddr => (others => '0'),
115  yiaddrTemp => (others => '0'),
116  siaddrTemp => (others => '0'),
117  index => 0,
118  valid => (others => '0'),
119  opCode => (others => '0'),
120  len => (others => '0'),
121  msgType => (others => '0'),
122  rxSlave => AXI_STREAM_SLAVE_INIT_C,
123  txMaster => AXI_STREAM_MASTER_INIT_C,
124  decode => CODE_S,
125  state => IDLE_S);
126 
127  signal r : RegType := REG_INIT_C;
128  signal rin : RegType;
129 
130  signal rxMaster : AxiStreamMasterType;
131  signal rxSlave : AxiStreamSlaveType;
132  signal txMaster : AxiStreamMasterType;
133  signal txSlave : AxiStreamSlaveType;
134 
135  -- attribute dont_touch : string;
136  -- attribute dont_touch of r : signal is "TRUE";
137 
138 begin
139 
140  FIFO_RX : entity work.AxiStreamFifoV2
141  generic map (
142  -- General Configurations
143  TPD_G => TPD_G,
144  INT_PIPE_STAGES_G => 0,
145  PIPE_STAGES_G => 0,
146  SLAVE_READY_EN_G => true,
147  VALID_THOLD_G => 1,
148  -- FIFO configurations
149  BRAM_EN_G => false,
150  USE_BUILT_IN_G => false,
151  GEN_SYNC_FIFO_G => true,
152  CASCADE_SIZE_G => 1,
153  FIFO_ADDR_WIDTH_G => 4,
154  -- AXI Stream Port Configurations
156  MASTER_AXI_CONFIG_G => DHCP_CONFIG_C)
157  port map (
158  -- Slave Port
159  sAxisClk => clk,
160  sAxisRst => rst,
163  -- Master Port
164  mAxisClk => clk,
165  mAxisRst => rst,
166  mAxisMaster => rxMaster,
167  mAxisSlave => rxSlave);
168 
169  comb : process (localIp, localMac, r, rst, rxMaster, txSlave) is
170  variable v : RegType;
171  variable tData : slv(7 downto 0);
172  variable tKeep : slv(15 downto 0);
173  begin
174  -- Latch the current value
175  v := r;
176 
177  -- Reset the flags
178  v.heartbeat := '0';
179  v.rxSlave := AXI_STREAM_SLAVE_INIT_C;
180  if txSlave.tReady = '1' then
181  v.txMaster.tValid := '0';
182  v.txMaster.tLast := '0';
183  v.txMaster.tUser := (others => '0');
184  end if;
185 
186  -- Check 1 second heartbeat timeout
187  if r.timer = (TIMER_1_SEC_C-1) then
188  -- Reset the counter
189  v.timer := 0;
190  -- Set the timeout flag
191  v.heartbeat := '1';
192  else
193  -- Increment the counter
194  v.timer := r.timer + 1;
195  end if;
196 
197  -- Check for heart beat
198  if (r.heartbeat = '1') then
199  -- Check Communication timer
200  if (r.commCnt /= 0) then
201  -- Decrement the counter
202  v.commCnt := r.commCnt - 1;
203  end if;
204  -- Check DHCP renewal timer
205  if (r.renewCnt /= 0) then
206  -- Decrement the counter
207  v.renewCnt := r.renewCnt - 1;
208  end if;
209  -- Check DHCP lease timer
210  if (r.leaseCnt /= 0) then
211  -- Decrement the counter
212  v.leaseCnt := r.leaseCnt - 1;
213  -- Check for DHCP lease expire event
214  if (r.leaseCnt = 1) then
215  -- Set the flag
216  v.dhcpReq := '0';
217  -- Reset the renewal counter
218  v.renewCnt := (others => '0');
219  -- Broadcast the MAC/IP addresses
220  v.remoteMac := (others => '1');
221  v.remoteIp := (others => '1');
222  end if;
223  else
224  -- Set DHCP IP address to local value
225  v.dhcpIP := localIp;
226  end if;
227  end if;
228 
229  -- Update the variables
230  tData := rxMaster.tData((8*r.index)+7 downto (8*r.index));
231  tKeep := x"000" & rxMaster.tKeep(3 downto 0);
232 
233  -- State Machine
234  case r.state is
235  ----------------------------------------------------------------------
236  when IDLE_S =>
237  -- Check for inbound data
238  if (rxMaster.tValid = '1') then
239  -- Accept the data
240  v.rxSlave.tReady := '1';
241  -- Check for SOF with no EOF
242  if (ssiGetUserSof(DHCP_CONFIG_C, rxMaster) = '1') and (rxMaster.tLast = '0') then
243  -- Check for valid DHCP server OP/HTYPE/HLEN/HOPS
244  if rxMaster.tData(31 downto 0) = SERVER_HDR_C then
245  -- Preset the counter
246  v.cnt := 1;
247  -- Next state
248  v.state := BOOTP_S;
249  end if;
250  end if;
251  else
252  -- Check DHCP renewal timer
253  if (r.renewCnt = 0) then
254  -- Check for communication timeout
255  if r.commCnt = 0 then
256  -- Reset the counter
257  v.cnt := 0;
258  -- Increment the counter
259  v.xid := r.xid + 1;
260  -- Next state
261  v.state := REQ_S;
262  end if;
263  end if;
264  end if;
265  ----------------------------------------------------------------------
266  when REQ_S =>
267  -- Check if ready to move data
268  if v.txMaster.tValid = '0' then
269  -- Set the default value
270  v.txMaster.tValid := '1';
271  v.txMaster.tData(31 downto 0) := (others => '0');
272  -- Increment the counter
273  v.cnt := r.cnt + 1;
274  -- Check the counter
275  case r.cnt is
276  -- OP/HTYPE/HLEN/HOPS
277  when 0 =>
278  v.txMaster.tData(31 downto 0) := CLIENT_HDR_C;
279  ssiSetUserSof(DHCP_CONFIG_C, v.txMaster, '1');
280  -- XID
281  when 1 =>
282  -- Save the XID as big Endian
283  v.txMaster.tData(31 downto 24) := r.xid(7 downto 0);
284  v.txMaster.tData(23 downto 16) := r.xid(15 downto 8);
285  v.txMaster.tData(15 downto 8) := r.xid(23 downto 16);
286  v.txMaster.tData(7 downto 0) := r.xid(31 downto 24);
287  -- SECS/FLAGS
288  when 2 =>
289  v.txMaster.tData(31 downto 0) := x"00080000";
290  -- SIADDR
291  when 5 =>
292  -- Check for DHCP request
293  if r.dhcpReq = '1' then
294  v.txMaster.tData(31 downto 0) := r.siaddr;
295  end if;
296  -- CHADDR[31:0]
297  when 7 =>
298  v.txMaster.tData(31 downto 0) := localMac(31 downto 0);
299  -- CHADDR[47:32]
300  when 8 =>
301  v.txMaster.tData(15 downto 0) := localMac(47 downto 32);
302  -- Magic cookie
303  when 59 =>
304  v.txMaster.tData(31 downto 0) := MAGIC_COOKIE_C;
305  -- DHCP Discover
306  when 60 =>
307  -- Check for DHCP Discover
308  if r.dhcpReq = '0' then
309  v.txMaster.tData(7 downto 0) := toSlv(53, 8); -- code = DHCP Message Type
310  v.txMaster.tData(15 downto 8) := x"01"; -- len = 1 byte
311  v.txMaster.tData(23 downto 16) := x"01"; -- DHCP Discover = 0x1
312  v.txMaster.tLast := '1';
313  -- Start the communication timer
314  v.commCnt := COMM_TIMEOUT_C;
315  -- Reset the counter
316  v.cnt := 0;
317  -- Next state
318  v.state := IDLE_S;
319  else
320  v.txMaster.tData(7 downto 0) := toSlv(53, 8); -- code = DHCP Message Type
321  v.txMaster.tData(15 downto 8) := x"01"; -- len = 1 byte
322  v.txMaster.tData(23 downto 16) := x"03"; -- DHCP request = 0x3
323  end if;
324  -- Requested IP address[15:0]
325  when 61 =>
326  v.txMaster.tData(7 downto 0) := toSlv(50, 8); -- code = Requested IP address
327  v.txMaster.tData(15 downto 8) := x"04"; -- len = 4 byte
328  v.txMaster.tData(31 downto 16) := r.yiaddr(15 downto 0); -- YIADDR[15:0]
329  -- Requested IP address[32:16]
330  when 62 =>
331  v.txMaster.tData(15 downto 0) := r.yiaddr(31 downto 16); -- YIADDR[31:16]
332  -- Server Identifier[15:0]
333  when 63 =>
334  v.txMaster.tData(7 downto 0) := toSlv(54, 8); -- code = Server Identifier
335  v.txMaster.tData(15 downto 8) := x"04"; -- len = 4 byte
336  v.txMaster.tData(31 downto 16) := r.siaddr(15 downto 0); -- SIADDR[15:0]
337  -- Server Identifier[32:16]
338  when 64 =>
339  v.txMaster.tData(15 downto 0) := r.siaddr(31 downto 16); -- SIADDR[31:16]
340  v.txMaster.tLast := '1';
341  -- Start the communication timer
342  v.commCnt := COMM_TIMEOUT_C;
343  -- Reset the counter
344  v.cnt := 0;
345  -- Next state
346  v.state := IDLE_S;
347  when others =>
348  null;
349  end case;
350  end if;
351  ----------------------------------------------------------------------
352  when BOOTP_S =>
353  -- Check for request data
354  if (rxMaster.tValid = '1') then
355  -- Accept the data
356  v.rxSlave.tReady := '1';
357  -- Increment the counter
358  v.cnt := r.cnt + 1;
359  -- Check the counter
360  case r.cnt is
361  -- XID
362  when 1 =>
363  -- Check if XID doesn't match
364  if (rxMaster.tData(31 downto 24) /= r.xid(7 downto 0))
365  or (rxMaster.tData(23 downto 16) /= r.xid(15 downto 8))
366  or (rxMaster.tData(15 downto 8) /= r.xid(23 downto 16))
367  or (rxMaster.tData(7 downto 0) /= r.xid(31 downto 24)) then
368  -- Next state
369  v.state := IDLE_S;
370  end if;
371  -- YIADDR
372  when 4 =>
373  v.yiaddrTemp := rxMaster.tData(31 downto 0);
374  -- SIADDR
375  when 5 =>
376  v.siaddrTemp := rxMaster.tData(31 downto 0);
377  -- CHADDR[31:0]
378  when 7 =>
379  -- Check if CHADDR[31:0] doesn't match
380  if rxMaster.tData(31 downto 0) /= localMac(31 downto 0) then
381  -- Next state
382  v.state := IDLE_S;
383  end if;
384  -- CHADDR[47:32]
385  when 8 =>
386  -- Check if CHADDR[47:32] doesn't match
387  if rxMaster.tData(15 downto 0) /= localMac(47 downto 32) then
388  -- Next state
389  v.state := IDLE_S;
390  end if;
391  -- Magic cookie
392  when 59 =>
393  -- Check if Magic cookie doesn't match
394  if rxMaster.tData(31 downto 0) /= MAGIC_COOKIE_C then
395  -- Next state
396  v.state := IDLE_S;
397  else
398  -- Reset data fields
399  v.valid := (others => '0');
400  v.index := 0;
401  v.decode := CODE_S;
402  -- Next state
403  v.state := DHCP_S;
404  end if;
405  when others =>
406  null;
407  end case;
408  -- Check for early packet termination
409  if (rxMaster.tLast = '1') then
410  -- Next state
411  v.state := IDLE_S;
412  end if;
413  end if;
414  ----------------------------------------------------------------------
415  when DHCP_S =>
416  -- Check for request data
417  if (rxMaster.tValid = '1') then
418  -- Decode State Machine
419  case r.decode is
420  ----------------------------------------------------------------
421  when CODE_S =>
422  -- Save the OP-code value
423  v.opCode := tData;
424  -- Check for not "PAD" and not "End" OP-code
425  if (tData /= 0) and (tData /= 255) then
426  -- Next state
427  v.decode := LEN_S;
428  end if;
429  ----------------------------------------------------------------
430  when LEN_S =>
431  -- Save the length
432  v.len := tData;
433  -- Check for non-zero length
434  if tData /= 0 then
435  -- Next state
436  v.decode := DATA_S;
437  else -- Error detected
438  -- Next state
439  v.state := IDLE_S;
440  end if;
441  ----------------------------------------------------------------
442  when DATA_S =>
443  -- Decrement the counter
444  v.len := r.len - 1;
445  -- Check for last byte
446  if (r.len = 1) then
447  -- Next state
448  v.decode := CODE_S;
449  end if;
450  -- Check the Code
451  if (r.opCode = 53) then -- Note: Assuming zero padding
452  -- Check for DHCP Message Type
453 
454  if (r.len = 1) then
455  -- Set the flag
456  v.valid(0) := '1';
457  -- Save the message
458  v.msgType := tData;
459  end if;
460  -- Check for IP address Lease Time
461  elsif (r.opcode = 51) then
462  if r.len = 4 then
463  -- Set the flag
464  v.valid(1) := '1';
465  v.leaseTime(31 downto 24) := tData;
466  elsif r.len = 3 then
467  -- Set the flag
468  v.valid(2) := '1';
469  v.leaseTime(23 downto 16) := tData;
470  elsif r.len = 2 then
471  -- Set the flag
472  v.valid(3) := '1';
473  v.leaseTime(15 downto 8) := tData;
474  elsif r.len = 1 then
475  -- Set the flag
476  v.valid(4) := '1';
477  v.leaseTime(7 downto 0) := tData;
478  end if;
479  end if;
480 
481  ----------------------------------------------------------------
482  end case;
483  -- Check the counter
484  if r.index = 3 then
485  -- Reset the counter
486  v.index := 0;
487  -- Accept the data
488  v.rxSlave.tReady := '1';
489  else
490  v.index := r.index + 1;
491  end if;
492  -- Check for last transfer
493  if (rxMaster.tLast = '1') and (getTKeep(tKeep) = (r.index+1)) then
494  -- Check for no EOFE
495  if ssiGetUserEofe(DHCP_CONFIG_C, rxMaster) = '0' then
496  -- Next state
497  v.state := VERIFY_S;
498  else
499  -- Next state
500  v.state := IDLE_S;
501  end if;
502  end if;
503  end if;
504  ----------------------------------------------------------------------
505  when VERIFY_S =>
506  -- Check if armed
507  if uAnd(r.valid) = '1' then
508  -- Save the values
509  v.yiaddr := r.yiaddrTemp;
510  v.siaddr := r.siaddrTemp;
511  -- Check for "DHCP Discover" request and "DHCP Offer" reply
512  if (r.dhcpReq = '0') and (r.msgType = 2) then
513  -- Set the flag
514  v.dhcpReq := '1';
515  -- Reset counter to immediately start the "DHCP request"
516  v.commCnt := 0;
517  -- Check for "DHCP request" request and "DHCP ACK" reply
518  elsif (r.dhcpReq = '1') and (r.msgType = 5) then
519  -- Set the DHCP address
520  v.dhcpIp := r.yiaddrTemp;
521  -- Clients begin to attempt to renew their leases
522  -- once half the lease interval has expired.
523  v.renewCnt := r.leaseTime(31 downto 1);
524  v.leaseCnt := r.leaseTime;
525  end if;
526  end if;
527  -- Next state
528  v.state := IDLE_S;
529 ----------------------------------------------------------------------
530  end case;
531 
532  -- Reset
533  if (rst = '1') then
534  v := REG_INIT_C;
535  end if;
536 
537  -- Register the variable for next clock cycle
538  rin <= v;
539 
540  -- Outputs
541  rxSlave <= v.rxSlave;
542  txMaster <= r.txMaster;
543  dhcpIp <= r.dhcpIp;
544 
545  end process comb;
546 
547  seq : process (clk) is
548  begin
549  if rising_edge(clk) then
550  r <= rin after TPD_G;
551  end if;
552  end process seq;
553 
554  FIFO_TX : entity work.AxiStreamFifoV2
555  generic map (
556  -- General Configurations
557  TPD_G => TPD_G,
558  INT_PIPE_STAGES_G => 0,
559  PIPE_STAGES_G => 0,
560  SLAVE_READY_EN_G => true,
561  VALID_THOLD_G => 1,
562  -- FIFO configurations
563  BRAM_EN_G => false,
564  USE_BUILT_IN_G => false,
565  GEN_SYNC_FIFO_G => true,
566  CASCADE_SIZE_G => 1,
567  FIFO_ADDR_WIDTH_G => 4,
568  -- AXI Stream Port Configurations
569  SLAVE_AXI_CONFIG_G => DHCP_CONFIG_C,
571  port map (
572  -- Slave Port
573  sAxisClk => clk,
574  sAxisRst => rst,
575  sAxisMaster => txMaster,
576  sAxisSlave => txSlave,
577  -- Master Port
578  mAxisClk => clk,
579  mAxisRst => rst,
582 
583 end rtl;
FIFO_ADDR_WIDTH_Ginteger range 4 to 48:= 9
out dhcpIpslv( 31 downto 0)
in ibDhcpMasterAxiStreamMasterType
TPD_Gtime := 1 ns
PIPE_STAGES_Gnatural range 0 to 16:= 1
out obDhcpMasterAxiStreamMasterType
std_logic sl
Definition: StdRtlPkg.vhd:28
AxiStreamMasterType :=(tValid => '0',tData =>( others => '0'),tStrb =>( others => '1'),tKeep =>( others => '1'),tLast => '0',tDest =>( others => '0'),tId =>( others => '0'),tUser =>( others => '0')) AXI_STREAM_MASTER_INIT_C
SLAVE_AXI_CONFIG_GAxiStreamConfigType := AXI_STREAM_CONFIG_INIT_C
slv( 15 downto 0) tKeep
SLAVE_READY_EN_Gboolean := true
GEN_SYNC_FIFO_Gboolean := false
out ibDhcpSlaveAxiStreamSlaveType
slv( 127 downto 0) tData
in localIpslv( 31 downto 0)
in localMacslv( 47 downto 0)
INT_PIPE_STAGES_Gnatural range 0 to 16:= 0
AxiStreamSlaveType :=(tReady => '0') AXI_STREAM_SLAVE_INIT_C
BRAM_EN_Gboolean := true
CLK_FREQ_Greal := 156.25E+06
slv( 127 downto 0) tUser
TPD_Gtime := 1 ns
sl valid
Definition: SsiPkg.vhd:66
out sAxisSlaveAxiStreamSlaveType
COMM_TIMEOUT_Gpositive := 30
in sAxisMasterAxiStreamMasterType
out mAxisMasterAxiStreamMasterType
in mAxisSlaveAxiStreamSlaveType
CASCADE_SIZE_Ginteger range 1 to ( 2** 24):= 1
USE_BUILT_IN_Gboolean := false
VALID_THOLD_Ginteger range 0 to ( 2** 24):= 1
_library_ ieeeieee
MASTER_AXI_CONFIG_GAxiStreamConfigType := AXI_STREAM_CONFIG_INIT_C
AxiStreamConfigType :=(TSTRB_EN_C => false,TDATA_BYTES_C => 16,TDEST_BITS_C => 8,TID_BITS_C => 0,TKEEP_MODE_C => TKEEP_COMP_C,TUSER_BITS_C => 4,TUSER_MODE_C => TUSER_FIRST_LAST_C) EMAC_AXIS_CONFIG_C
Definition: EthMacPkg.vhd:58
std_logic_vector slv
Definition: StdRtlPkg.vhd:29
in obDhcpSlaveAxiStreamSlaveType