You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
331 lines
11 KiB
331 lines
11 KiB
------------------------------------------------------------------------------
|
|
-- --
|
|
-- Copyright (C) 2015, AdaCore --
|
|
-- --
|
|
-- Redistribution and use in source and binary forms, with or without --
|
|
-- modification, are permitted provided that the following conditions are --
|
|
-- met: --
|
|
-- 1. Redistributions of source code must retain the above copyright --
|
|
-- notice, this list of conditions and the following disclaimer. --
|
|
-- 2. Redistributions in binary form must reproduce the above copyright --
|
|
-- notice, this list of conditions and the following disclaimer in --
|
|
-- the documentation and/or other materials provided with the --
|
|
-- distribution. --
|
|
-- 3. Neither the name of the copyright holder nor the names of its --
|
|
-- contributors may be used to endorse or promote products derived --
|
|
-- from this software without specific prior written permission. --
|
|
-- --
|
|
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS --
|
|
-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT --
|
|
-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR --
|
|
-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT --
|
|
-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, --
|
|
-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT --
|
|
-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, --
|
|
-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY --
|
|
-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT --
|
|
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE --
|
|
-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --
|
|
-- --
|
|
------------------------------------------------------------------------------
|
|
with STM32.GPIO;
|
|
with STM32.Device;
|
|
with STM32_SVD.RCC;
|
|
with STM32_SVD.SYSCFG;
|
|
with STM32_SVD.Ethernet; use STM32_SVD.Ethernet;
|
|
with STM32.SDRAM;
|
|
with Ada.Real_Time;
|
|
with Ada.Interrupts.Names;
|
|
with Ada.Unchecked_Conversion;
|
|
|
|
package body STM32.Eth is
|
|
|
|
type Rx_Desc_Range is mod 16;
|
|
type Rx_Desc_Array is array (Rx_Desc_Range) of Rx_Desc_Type;
|
|
type Rx_Desc_Arr_Ptr is access Rx_Desc_Array;
|
|
|
|
type Rx_Buffer is array (Natural range 0 .. 1023) of Unsigned_8;
|
|
type Rx_Buffer_Array is array (Rx_Desc_Range) of Rx_Buffer;
|
|
type Rx_Buffer_Arr_Ptr is access Rx_Buffer_Array;
|
|
|
|
Rx_Descs : Rx_Desc_Arr_Ptr;
|
|
Rx_Buffs : Rx_Buffer_Arr_Ptr;
|
|
|
|
procedure Init_Rx_Desc (I : Rx_Desc_Range);
|
|
|
|
---------------------
|
|
-- Initialize_RMII --
|
|
---------------------
|
|
|
|
procedure Initialize_RMII
|
|
is
|
|
use STM32.GPIO;
|
|
use STM32.Device;
|
|
use STM32_SVD.RCC;
|
|
begin
|
|
-- Enable GPIO clocks
|
|
|
|
Enable_Clock (GPIO_A);
|
|
Enable_Clock (GPIO_C);
|
|
Enable_Clock (GPIO_G);
|
|
|
|
-- Enable SYSCFG clock
|
|
RCC_Periph.APB2ENR.SYSCFGEN := True;
|
|
|
|
-- Select RMII (before enabling the clocks)
|
|
STM32_SVD.SYSCFG.SYSCFG_Periph.PMC.MII_RMII_SEL := True;
|
|
|
|
Configure_Alternate_Function (PA1, GPIO_AF_ETH_11); -- RMII_REF_CLK
|
|
Configure_Alternate_Function (PA2, GPIO_AF_ETH_11); -- RMII_MDIO
|
|
Configure_Alternate_Function (PA7, GPIO_AF_ETH_11); -- RMII_CRS_DV
|
|
Configure_Alternate_Function (PC1, GPIO_AF_ETH_11); -- RMII_MDC
|
|
Configure_Alternate_Function (PC4, GPIO_AF_ETH_11); -- RMII_RXD0
|
|
Configure_Alternate_Function (PC5, GPIO_AF_ETH_11); -- RMII_RXD1
|
|
Configure_Alternate_Function (PG2, GPIO_AF_ETH_11); -- RMII_RXER
|
|
Configure_Alternate_Function (PG11, GPIO_AF_ETH_11); -- RMII_TX_EN
|
|
Configure_Alternate_Function (PG13, GPIO_AF_ETH_11); -- RMII_TXD0
|
|
Configure_Alternate_Function (PG14, GPIO_AF_ETH_11); -- RMII_TXD1
|
|
Configure_IO (PA1, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
Configure_IO (PA2, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
Configure_IO (PA7, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
Configure_IO (PC1, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
Configure_IO (PC4, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
Configure_IO (PC5, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
Configure_IO (PG2, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
Configure_IO (PG11, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
Configure_IO (PG13, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
Configure_IO (PG14, (Mode_AF, Push_Pull, Speed_100MHz, Floating));
|
|
|
|
-- Enable clocks
|
|
RCC_Periph.AHB1ENR.ETHMACEN := True;
|
|
RCC_Periph.AHB1ENR.ETHMACTXEN := True;
|
|
RCC_Periph.AHB1ENR.ETHMACRXEN := True;
|
|
RCC_Periph.AHB1ENR.ETHMACPTPEN := True;
|
|
|
|
-- Reset
|
|
RCC_Periph.AHB1RSTR.ETHMACRST := True;
|
|
RCC_Periph.AHB1RSTR.ETHMACRST := False;
|
|
|
|
-- Software reset
|
|
Ethernet_DMA_Periph.DMABMR.SR := True;
|
|
while Ethernet_DMA_Periph.DMABMR.SR loop
|
|
null;
|
|
end loop;
|
|
end Initialize_RMII;
|
|
|
|
--------------
|
|
-- Read_MMI --
|
|
--------------
|
|
|
|
procedure Read_MMI (Reg : UInt5; Val : out UInt16)
|
|
is
|
|
use Ada.Real_Time;
|
|
Pa : constant UInt5 := 0;
|
|
Cr : UInt3;
|
|
begin
|
|
case STM32.Device.System_Clock_Frequencies.HCLK is
|
|
when 20e6 .. 35e6 - 1 => Cr := 2#010#;
|
|
when 35e6 .. 60e6 - 1 => Cr := 2#011#;
|
|
when 60e6 .. 100e6 - 1 => Cr := 2#000#;
|
|
when 100e6 .. 150e6 - 1 => Cr := 2#001#;
|
|
when 150e6 .. 216e6 => Cr := 2#100#;
|
|
when others => raise Constraint_Error;
|
|
end case;
|
|
|
|
Ethernet_MAC_Periph.MACMIIAR :=
|
|
(PA => Pa,
|
|
MR => Reg,
|
|
CR => Cr,
|
|
MW => False,
|
|
MB => True,
|
|
others => <>);
|
|
loop
|
|
exit when not Ethernet_MAC_Periph.MACMIIAR.MB;
|
|
delay until Clock + Milliseconds (1);
|
|
end loop;
|
|
|
|
Val := Ethernet_MAC_Periph.MACMIIDR.TD;
|
|
end Read_MMI;
|
|
|
|
------------------
|
|
-- Init_Rx_Desc --
|
|
------------------
|
|
|
|
procedure Init_Rx_Desc (I : Rx_Desc_Range)
|
|
is
|
|
function W is new Ada.Unchecked_Conversion
|
|
(Address, UInt32);
|
|
Last : constant Boolean := I = Rx_Desc_Range'Last;
|
|
begin
|
|
Rx_Descs (I) :=
|
|
(Rdes0 => (Own => 1, others => <>),
|
|
Rdes1 => (Dic => 0, Rbs2 => 0,
|
|
Rer => (if Last then 1 else 0),
|
|
Rch => 1, Rbs => Rx_Buffer'Length,
|
|
others => <>),
|
|
Rdes2 => W (Rx_Buffs (I)'Address),
|
|
Rdes3 => W (Rx_Descs (I + 1)'Address));
|
|
end Init_Rx_Desc;
|
|
|
|
procedure Init_Mac is
|
|
function To_Rx_Desc_Arr_Ptr is new Ada.Unchecked_Conversion
|
|
(System.Address, Rx_Desc_Arr_Ptr);
|
|
function To_Rx_Buffer_Arr_Ptr is new Ada.Unchecked_Conversion
|
|
(System.Address, Rx_Buffer_Arr_Ptr);
|
|
function W is new Ada.Unchecked_Conversion
|
|
(Address, UInt32);
|
|
Desc_Addr : Address;
|
|
begin
|
|
-- FIXME: check speed, full duplex
|
|
Ethernet_MAC_Periph.MACCR :=
|
|
(CSTF => True,
|
|
WD => False,
|
|
JD => False,
|
|
IFG => 2#100#,
|
|
CSD => False,
|
|
FES => True,
|
|
ROD => True,
|
|
LM => False,
|
|
DM => True,
|
|
IPCO => False,
|
|
RD => False,
|
|
APCS => True,
|
|
BL => 2#10#,
|
|
DC => True,
|
|
TE => False,
|
|
RE => False,
|
|
others => <>);
|
|
Ethernet_MAC_Periph.MACFFR :=
|
|
(RA => True, others => <>);
|
|
Ethernet_MAC_Periph.MACHTHR := 0;
|
|
Ethernet_MAC_Periph.MACHTLR := 0;
|
|
Ethernet_MAC_Periph.MACFCR :=
|
|
(PT => 0,
|
|
ZQPD => False,
|
|
PLT => 0,
|
|
UPFD => False,
|
|
RFCE => True,
|
|
TFCE => True,
|
|
FCB => False,
|
|
others => <>);
|
|
Ethernet_MAC_Periph.MACVLANTR :=
|
|
(VLANTC => False,
|
|
VLANTI => 0,
|
|
others => <>);
|
|
Ethernet_MAC_Periph.MACPMTCSR :=
|
|
(WFFRPR => False,
|
|
GU => False,
|
|
WFR => False,
|
|
MPR => False,
|
|
WFE => False,
|
|
MPE => False,
|
|
PD => False,
|
|
others => <>);
|
|
|
|
Desc_Addr := STM32.SDRAM.Reserve (Amount => Rx_Desc_Array'Size / 8);
|
|
Rx_Descs := To_Rx_Desc_Arr_Ptr (Desc_Addr);
|
|
Ethernet_DMA_Periph.DMARDLAR := W (Desc_Addr);
|
|
|
|
Desc_Addr := STM32.SDRAM.Reserve (Amount => Rx_Buffer_Array'Size / 8);
|
|
Rx_Buffs := To_Rx_Buffer_Arr_Ptr (Desc_Addr);
|
|
|
|
for I in Rx_Desc_Range loop
|
|
Init_Rx_Desc (I);
|
|
end loop;
|
|
|
|
Ethernet_DMA_Periph.DMABMR :=
|
|
(SR => False,
|
|
DA => False,
|
|
DSL => 0,
|
|
EDFE => False,
|
|
PBL => 4,
|
|
RTPR => 0,
|
|
FB => True,
|
|
RDP => 4,
|
|
USP => True,
|
|
FPM => False,
|
|
AAB => False,
|
|
MB => False,
|
|
others => <>);
|
|
end Init_Mac;
|
|
|
|
protected Sync is
|
|
entry Wait_Packet;
|
|
procedure Start_Rx;
|
|
|
|
procedure Interrupt;
|
|
pragma Attach_Handler (Interrupt, Ada.Interrupts.Names.ETH_Interrupt);
|
|
private
|
|
Rx_Pkt : Boolean := False;
|
|
|
|
-- First descriptor of the last received packet.
|
|
Last_Rx : Rx_Desc_Range;
|
|
|
|
-- Descriptor for the next packet to be received.
|
|
Next_Rx : Rx_Desc_Range;
|
|
end Sync;
|
|
|
|
protected body Sync is
|
|
procedure Start_Rx is
|
|
begin
|
|
-- Make as if last packet received was in last descriptor.
|
|
Last_Rx := Rx_Desc_Range'Last;
|
|
Next_Rx := Rx_Desc_Range'First;
|
|
Rx_Descs (Last_Rx).Rdes0.Own := 0;
|
|
Rx_Descs (Last_Rx).Rdes0.Ls := 1;
|
|
|
|
-- Assume the RxDMA is ok.
|
|
Ethernet_MAC_Periph.MACCR.RE := True;
|
|
Ethernet_DMA_Periph.DMAIER.RIE := True;
|
|
Ethernet_DMA_Periph.DMAIER.NISE := True;
|
|
Ethernet_DMA_Periph.DMAOMR.SR := True;
|
|
Ethernet_DMA_Periph.DMARPDR := 1;
|
|
end Start_Rx;
|
|
|
|
entry Wait_Packet when Rx_Pkt is
|
|
begin
|
|
-- Set OWN to last rx descs.
|
|
loop
|
|
-- The previous packet is owned by the software.
|
|
pragma Assert (Rx_Descs (Last_Rx).Rdes0.Own = 0);
|
|
|
|
-- Refill desc.
|
|
Init_Rx_Desc (Last_Rx);
|
|
Last_Rx := Last_Rx + 1;
|
|
exit when Last_Rx = Next_Rx;
|
|
end loop;
|
|
|
|
-- As we got an interrupt, the next descriptor should be for us.
|
|
pragma Assert (Rx_Descs (Last_Rx).Rdes0.Own = 0);
|
|
Last_Rx := Next_Rx;
|
|
|
|
-- Find Next Rx.
|
|
loop
|
|
exit when Rx_Descs (Next_Rx).Rdes0.Ls = 1;
|
|
Next_Rx := Next_Rx + 1;
|
|
end loop;
|
|
Next_Rx := Next_Rx + 1;
|
|
|
|
if Rx_Descs (Next_Rx).Rdes0.Own = 1 then
|
|
-- Have to wait if no packets after the current one.
|
|
Rx_Pkt := False;
|
|
end if;
|
|
end Wait_Packet;
|
|
|
|
procedure Interrupt is
|
|
begin
|
|
Ethernet_DMA_Periph.DMASR.RS := True;
|
|
Rx_Pkt := True;
|
|
end Interrupt;
|
|
end Sync;
|
|
|
|
procedure Start_Rx is
|
|
begin
|
|
Sync.Start_Rx;
|
|
end Start_Rx;
|
|
|
|
procedure Wait_Packet is
|
|
begin
|
|
Sync.Wait_Packet;
|
|
end Wait_Packet;
|
|
end STM32.Eth;
|
|
|