DS80C400 Ethernet Drivers
要約
The DS80C400 high-speed microcontroller has a built-in Ethernet media-access controller (MAC) with an industry-standard media independent interface (MII). This application note presents design considerations and fully tested example assembly code for an Ethernet interrupt handler, and code for sending and receiving Ethernet packets. Using these routines, you can develop custom applications such as TCP/IP routers.
Introduction
The DS80C400 high-speed microcontroller has a built-in Ethernet media-access controller (MAC) with an industry-standard media independent interface (MII). Please refer to the High-Speed Microcontroller User's Guide: DS80C400 Supplement and the DS80C400 data sheet for details.
This application note presents design considerations and fully tested example assembly code for an Ethernet interrupt handler, and code for sending and receiving Ethernet packets. Using these routines, you can develop custom application such as TCP/IP routers. Full source code and the header files defining the symbolic constants can be found on the Dallas Semiconductor ftp site here.
Figure 1. DS80C400 Ethernet Buffer.
The DS80C400 MAC Hardware
Ethernet Buffer Memory
The DS80C400 communicates with the network via a set of special function registers (SFRs) and 8kB of dual port buffer memory. The buffer memory is divided into the receive and send memory and can be addressed in blocks of 256 bytes ("pages"). The receive pages are organized in a circular fashion, managed by the DS80C400 hardware. The send buffer is managed by the user's application.
The location for the Ethernet buffer is usually address 0FFE000h
(default configuration established by ROM loader), assigned to the constant ETH_RECEIVE_BUFFER
.
Ethernet Control Status Registers
The primitives ReadCSR and WriteCSR are used to read and write the DS80C400 Ethernet control status registers (CSRs). Note that the example code does not save the processor registers across function calls. When using this code, ensure that you don't destroy the processor state (this is especially important when using interrupt driven data transfer).
Read CSR
ReadCSR
reads a control status register.
;**************************************************************************** ;* ;* Function Name: ETH_ReadCSR ;* ;* Description: Read from specified register. ;* ;* Input(s): a -> register address ;* ;* Outputs(s): r3:r2:r1:r0 -> 32 bit register byte value ;* ;**************************************************************************** ETH_ReadCSR: push eie clr eie.5 mov csra, a ; Load CSRA SFR with the LSB of the ; 16-bit address of the targeted CSR anl bcuc, #0f0h ; Clear BCUC command bits orl bcuc, #BCU_READ_CSR ; Write read CSR command to BCUC SFR push acc eth_readcsr_busy: ; Wait until Busy bit in BCUC SFR is reset mov a, bcuc ; Move to acc since BCUC is not bit cap. jb acc.7, eth_readcsr_busy pop acc mov r3, csrd ; Read CSRD SFR for MSB of 32 bit data mov r2, csrd mov r1, csrd mov r0, csrd ; LSB pop eie ret
Listing 1. ReadCSR
Reads a Control Status Register
Note that this code saves, disables, and restores the Ethernet activity interrupt enable (eie.5
) to make sure that a write to the CSR is not interrupted by an Ethernet activity interrupt. The definition for the bcuc, csrd
and csra
SFRs can be found in the include file ds80c400.inc
. Constant values such as BCUC_READ_CSR
are defined in eth400.inc
.
Write CSR
The WriteCSR
function writes a 32 bit value to a control status register.
;**************************************************************************** ;* ;* Function Name: ETH_WriteCSR ;* ;* Description: Write to specified register. ;* ;* Input(s): a -> register address ;* r3:r2:r1:r0 -> 32 bit value ;* ;* Outputs(s): N/A ;* ;**************************************************************************** ETH_WriteCSR: push eie clr eie.5 mov csrd, r3 ; Write CSRD SFR for MSB of 32 bit data mov csrd, r2 mov csrd, r1 mov csrd, r0 ; LSB mov csra, a ; Load CSRA SFR with the LSB of the ; 16-bit address of the targeted CSR anl bcuc, #0f0h ; Clear bcuc command bits 0-3 orl bcuc, #BCU_WRITE_CSR ; Write write CSR command to bcuc SFR push acc eth_writecsr_busy: ; Wait until Busy bit in BCUC SFR is reset mov a, bcuc jb acc.7, eth_writecsr_busy pop acc pop eie ret
Listing 2. WriteCSR
Writes a Control Status Register
Initialization
MAC Address
In order to use the DS80C400 on the network, a globally unique MAC address needs to be programmed into the device. The MAC address can either be acquired from the DS2502-E48 MAC address 1-Wire® part (Dallas Semiconductor has registered a range of ready-to-go MAC addresses in order to simplify building embedded devices) or from another IEEE® registered source.
Very important: Under NO circumstances select a random MAC address or the address of another existing device. MAC addresses are globally unique and network stability depends on well behaved devices!
;**************************************************************************** ;* ;* Function Name: ETH_LoadEthernetAddress ;* ;* Description: Load the 48 bit ethernet address into the controller. ;* ;* Input(s): dptr0 -> pointer to the Ethernet address (big-endian) ; for example 00 60 01 02 03 04 ;* ;* Outputs(s): N/A ;* ;**************************************************************************** ETH_LoadEthernetAddress: movx a, @dptr mov r0, a inc dptr movx a, @dptr mov r1, a inc dptr movx a, @dptr mov r2, a inc dptr movx a, @dptr mov r3, a inc dptr mov a, #CSR_MAC_LO acall ETH_WriteCSR movx a, @dptr mov r0, a inc dptr movx a, @dptr mov r1, a clr a mov r2, a mov r3, a mov a, #CSR_MAC_HI acall ETH_WriteCSR ret
Listing 3. LoadEthernetAddress
Loads the MAC Address into the DS80C400
Note that two CSR writes are required to fully load the 6-byte Ethernet MAC address. Since this code is only called during initialization, it is not protected against Ethernet activity interrupts.
Initializing the Ethernet MAC further requires configuration of the partition between receive buffer (incoming packets) and send buffer (outgoing packets). Figure 1 shows this partition between page n-1 and page n.
To simplify code and avoid dropping inbound packets, most applications will benefit from partitioning the buffer memory in a fashion that reserves most of the pages for inbound packets and only allocates enough pages for one outbound packet. The reason for this is that Ethernet is a shared medium and—even in switched networks—only a fraction of incoming packets are of interest to an application. Therefore, we define the constants ETH_TRANSMIT_PAGE
to 17h and ETH_SEND_BUFFER
to ETH_RECEIVE_BUFFER
+ 17h × 256.
Constant | Value |
ETH_TRANSMIT_PAGE |
17h |
ETH_SEND_BUFFER |
0FFF700h |
The following code first disables the transmitter and then initializes the DS80C400 buffer memory to select the 23:9 receive:send partition. The code then sets the half/full duplex status (this status can be acquired from the MII, see below) and enables the transmitter.
Enabling the Transceiver
;**************************************************************************** ;* ;* Function Name: ETH_EnableTransceiver ;* ;* Description: Enable receiver and transmitter for Ethernet controller. ;* ;* Input(s): N/A ;* ;* Outputs(s): N/A ;* ;**************************************************************************** ETH_EnableTransceiver: push eie clr eie.5 ; First, disable transmitter and receiver (full duplex bit is ; not settable if they are on) clr a mov r3, a mov r2, a mov r1, a mov r0, a mov a, #CSR_MAC_CTRL acall ETH_WriteCSR ; Set Ethernet buffer sizes TIMEDACCESS mov ebs, #ETH_TRANSMIT_PAGE ; Also clears the flush filter failed bit mov r3, #00h ; Select non-byte swap mode mov dptr, #ETH_DUPLEX_STATUS movx a, @dptr swap a ; Move bit to position 4 (20:F) jnz eth_et_fullduplex orl a, #80h ; Disable receive own (23:DRO) eth_et_fullduplex: orl a, #08h ; Pass all multicast (19:PM) – OPTIONAL mov r2, a ; Set duplex mode according to PHY detection mov r1, #10h ; Perfect filtering of multicast, ; late collision control, no auto pad strip mov r0, #0ch ; Block-off limit 10, no deferral check, ; enable transmitter and receiver mov a, #CSR_MAC_CTRL acall ETH_WriteCSR pop eie ret
Listing 4. EnableTransceiver
Partitions the Buffer Memory and Enables the Transceiver
Note that this code assumes the duplex status information is stored at location ETH_DUPLEX_STATUS
in MOVX memory.
Flushing the Buffer
Next, the Ethernet buffer is flushed to ensure clean startup.
;**************************************************************************** ;* ;* Function Name: ETH_Flush ;* ;* Description: Release all resources. ;* ;* Input(s): N/A ;* ;* Outputs(s): N/A ;* ;**************************************************************************** ETH_Flush: anl bcuc, #0f0h ; Clear bcuc command bits orl bcuc, #BCU_INV_CURR ; Write release command to bcuc SFR ret
Listing 5. Flush Flushes the Receive Buffer
Sending and Receiving
Sending a Packet
To send a packet, the user's application must first place the packet data in the Ethernet send buffer. If a previous packet was placed at the same address, the application must wait for the transmit to be complete before modifying the buffer memory.
Note that the first four bytes of the send buffer are reserved for the send status word. The first byte that will be transmitted is at location ETH_SEND_BUFFER
+4.
;**************************************************************************** ;* ;* Function Name: ETH_Transmit ;* ;* Description: Transmit the raw Ethernet packet currently in the ;* Ethernet send buffer ;* ;* Input(s): r5:r4 = total packet length in bytes ;* ;* Outputs(s): N/A ;* ;**************************************************************************** ETH_Transmit: ; Ethernet frame is in transmit buffer (Starting at ; page offset = 4). Byte count is in r5:r4 ; Load MSB of byte count to bcud SFR mov bcud, r5 ; Load LSB of byte count to bcud SFR mov bcud, r4 ; Load starting page address to bcud SFR mov bcud, #ETH_TRANSMIT_PAGE ; XXX Set transmit in progress flag in your software here ; XXX so you can avoid interrupting a transmit in progress. ; XXX e.g.: setb ds400_xmit ; Write transmit request to bcuc SFR anl bcuc, #0f0h ; Clear bcuc command bits orl bcuc, #BCU_XMIT ; Write transmit command to bcuc SFR ret
Listing 6. Transmit
Sends a Packet Onto the Network
Receiving a Packet
When a packet is received (usually indicated by an interrupt, see below), the user code needs to unload the packet from the Ethernet buffer memory and then release the buffer memory, unlike the send buffer, which is managed by the user, the receive buffer is managed by the DS80C400.
Unloading the Packet Data
Note that a received packet can span several pages in the receive buffer and it can wrap from the last page in the receive buffer to the first page in the receive buffer. Ensure that your packet copy routine properly handles this case.
;**************************************************************************** ;* ;* Function Name: ETH_Receive ;* ;* Description: Start unloading the last packet from the ;* Ethernet controller. ;* ;* Input(s): N/A ;* ;* Outputs(s): N/A ;* ;**************************************************************************** ETH_Receive: ; Get location of buffer and set dptr0 accordingly mov a, bcud anl a, #1fh ; we are not interested in the page count ; so now a contains the starting page number ; (1 page is 256 bytes) mov dptr, #ETH_RECEIVE_BUFFER ; receive buffer starting address mov b, a ; "multiply" page by 256 to get byte count clr a acall Add_Dptr0_16 ; and add it to receive buffer starting address ; dptr0 now points to the receive status word of the packet movx a, @dptr inc dptr mov r2, a ; save LSB of frame length movx a, @dptr inc dptr mov r3, a ; save this ; check runt frame, watchdog time-out anl a, #(80h or 40h) jnz eth_ueh_release mov a, r3 ; restore and get frame length anl a, #3fh mov r3, a ; save HSB of frame length movx a, @dptr inc dptr ; check CRC error, MII error, collision seen, frame too long anl a, #(20h or 08h or 02h or 01h) jnz eth_ueh_release movx a, @dptr ; MSB of status word ; check for length error, control frame, unsupported ctrl frame ; missed frame mov b, a anl a, #(80h or 20h or 04h or 02h or 01h) jnz eth_ueh_release ; bad bad bad frame! mov a, b anl a, #40h ; check for filter match jz eth_ueh_release ; XXX Copy the packet into your buffer here. ; XXX r3:r2 contain the length of the packet, ; XXX dptr0 points to the beginning of the data. ; XXX Note that the buffer can wrap! eth_ueh_release: ret
Listing 7. Receive Receives a Packet from the Network
Releasing the Buffer
After processing an incoming packet, the user code needs to release the buffer memory in the Ethernet receive buffer.
;**************************************************************************** ;* ;* Function Name: ETH_Release ;* ;* Description: Release resources. ;* ;* Input(s): N/A ;* ;* Outputs(s): N/A ;* ;**************************************************************************** ETH_Release: anl bcuc, #0f0h ; Clear bcuc command bits orl bcuc, #BCU_INV_CURR ; Write release command to bcuc SFR ret
Listing 8. Release Releases a Packet from the Receive Buffer
Interrupt Driven Operation
Instead of polling the bit flags in the bcuc SFR, an application should use the Ethernet activity interrupt for better performance. There is one interrupt handler for both receive and transmit complete interrupts. The Ethernet activity interrupt calls location 000073h. Since there are only 8 bytes per interrupt, we suggest installing a long jump to the actual function:
org 73h ljmp ETH_ProcessInterrupt
Processing Interrupts
The following code handles both receive and transmit complete interrupts.
;**************************************************************************** ;* ;* Function Name: ETH_ProcessInterrupt ;* ;* Description: ISR for Ethernet interrupt ;* ;* Input(s): N/A ;* ;* Outputs(s): N/A ;* ;* Destroyed: Nothing. ;**************************************************************************** ETH_ProcessInterrupt: push acc mov a, bcuc anl a, #rif ; Received data? jz eth_pi_no_receive ; XXX Call your receive packet handler here. ; XXX Ensure it saves and restores all registers! ; XXX E.g.: acall ETH_ProcessPacket eth_pi_no_receive: mov a, bcuc anl a, #tif jz eth_pi_exit ; Transmitted data? ; XXX If you keep track of a send in progress, here's the place ; XXX to clear the flag. ; XXX E.g.: clr ds400_xmit anl bcuc, #(not(tif) and 0f0h) ; and NOOP command ; XXX If you keep transmit queue, send next packet from queue ; XXX E.g.: acall ETH_SendNextFromQueue eth_pi_exit: pop acc reti
Listing 9. ProcessInterrupt
Handles Ethernet Activity Interrupts
Enabling Interrupts
Finally, after enabling the Ethernet interrupt, the DS80C400 is ready to receive and send packets. ;**************************************************************************** ;* ;* Function Name: ETH_EnableInterrupts ;* ;* Description: Enable Ethernet transmit/receive interrupts. ;* ;* ;* Input(s): ;* ;* Outputs(s): ;* ;* Destroyed: ;**************************************************************************** ETH_EnableInterrupts: ; XXX If you keep track of transmits in progress, clear ; XXX the flag here. ; XXX E.g.: clr ds400_xmit anl bcuc, #(not(rif or tif) and 0f0h) ; Clear interrupt flags setb eie.5 ; Enable Ethernet activity interrupt clr eaip ; Set network interrupt priority low ret
Listing 10. EnableInterrupts
Enables the Ethernet Activity Interrupt
Media Independent Interface (MII)
The Media Independent Interface (MII) defines I/O lines that allow the DS80C400 to communicate with the physical layer interface (PHY). Even though many PHYs have a vendor-specific command set, there are common commands that most PHYs share, defined in the IEEE Std. 802.3. Communications with a PHY can be used to query a PHY for its auto negotiation and duplex state, and to isolate and "un-isolate" PHYs (in the case of multiple PHYs) and reconfigure a PHY.
The MII on the DS80C400 is accessed through CSR registers. The following routines read and write an MII register in a given PHY.
Read MII Register
;**************************************************************************** ;* ;* Function Name: ETH_ReadMII ;* ;* Description: Read MII register ;* ;* Input(s): a -> register number, b -> PHY number ;* ;* Outputs(s): r1:r0 -> contents of MII register ;* ;* Notes: MII address Register (14h): ;* 31-16 -- reserved ;* 15-11 -- PHY address ;* 10-6 -- MII register ;* 5-2 -- reserved ;* 1 -- MII write ;* 0 -- MII busy ;* ;**************************************************************************** ETH_ReadMII: push eie clr eie.5 mov r7, a ; Save register number ; Wait until MII is not busy eth_rmii_busy: mov a, #CSR_MII_ADDR acall ETH_ReadCSR mov a, r0 jb acc.0, eth_rmii_busy clr a mov r3, a ; Reserved - always clear mov r2, a mov a, r7 ; Restore register number rr a rr a ; And shift to pos 10:8 mov r7, a ; Save result of shift anl a, #07h ; Select bits 0:2 mov r1, a mov a, b ; Load PHY address anl a, #1fh rl a rl a rl a ; shift to 7:3 orl a, r1 mov r1, a mov a, r7 ; Restore result of shift anl a, #0c0h ; Select bits 7:6 mov r0, a mov a, #CSR_MII_ADDR acall ETH_WriteCSR ; Wait until MII is not busy eth_rmii_busy2: mov a, #CSR_MII_ADDR acall ETH_ReadCSR mov a, r0 jb acc.0, eth_rmii_busy2 ; Read MII data register mov a, #CSR_MII_DATA acall ETH_ReadCSR pop eie ret
Listing 11. ReadMII
Reads an MII Register from a Given PHY
Write MII Register
;**************************************************************************** ;* ;* Function Name: ETH_WriteMII ;* ;* Description: Write MII register ;* ;* Input(s): a -> register number, b -> PHY number, r1:r0 -> data ;* ;* Outputs(s): N/A ;* ;**************************************************************************** ETH_WriteMII: push eie clr eie.5 push 0 ; Save r1 and r0 push 1 mov r7, a ; Save register number ; Wait until MII is not busy eth_wmii_busy: mov a, #CSR_MII_ADDR acall ETH_ReadCSR mov a, r0 jb acc.0, eth_wmii_busy pop 1 pop 0 clr a mov r3, a ; Reserved - always clear mov r2, a ; Write MII data register mov a, #CSR_MII_DATA acall ETH_WriteCSR mov a, r7 ; Restore register number rr a rr a ; And shift to pos 0:2 mov r7, a ; Save result of shift anl a, #07h ; Select bits 0:2 mov r1, a mov a, b ; Load PHY address anl a, #1fh rl a rl a rl a ; shift to 7:3 orl a, r1 mov r1, a mov a, r7 ; Restore result of shift anl a, #0c0h ; Select bits 7:6 orl a, #2 ; Select write bit :1: mov r0, a mov a, #CSR_MII_ADDR acall ETH_WriteCSR pop eie ret
Listing 12. WriteMII
Writes an MII Register to a Given PHY
MII Example
The following code reads the MII status register of a PHY:
mov b, #0 mov a, #MII_STATUS acall ETH_ReadMII