{{
ENC28J60 Ethernet NIC / MAC Driver
----------------------------------
Copyright (C) 2006 - 2007 Harrison Pham
This file is part of PropTCP.
PropTCP is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
PropTCP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
----
Driver Framework / API derived from EDTP Framethrower Fundamental Driver by Fred Eady
Constant names / Theoretical Code Logic derived from Microchip Technology, Inc.'s enc28j60.c / enc28j60.h files
}}
CON
version = 3 ' major version
release = 2 ' minor version
OBJ
pause : "pause"
CON
' ***************************************
' ** ENC28J60 SRAM Defines **
' ***************************************
' Silicon Revision
silicon_rev = 4 ' required silicon revision (current is B5)
' ENC28J60 SRAM Usage Constants
MAXFRAME = 1518 ' 6 (src addr) + 6 (dst addr) + 2 (type) + 1500 (data) + 4 (FCS CRC) = 1518 bytes
TX_BUFFER_SIZE = 1518
TXSTART = 8192 - (TX_BUFFER_SIZE + 8)
TXEND = TXSTART + (TX_BUFFER_SIZE + 8)
CON
{ Heap explanation:
The heap is 4KB large, and is broken down into 128 32byte pages.
A page can either be a part of an allocation or a part
of free space. The first byte of the first page of either
free space or an allocation contains metadata. It is of the
following format:
┌─ Free/Alloc'd
├─Span Size─┤ * NOTE: Span size is number of pages minus one!
[7|6|5|4|3|2|1|0]
This byte is duplicated as the last byte of the last page
in an allocation or free space. This allows the heap to be
traversed in either direction.
When an allocation is requested, the allocator will step thru
the spans one by one until it finds a free span which is
greater than or equal to the requested number of pages. If it
is greater, the span is split in two.
When an allocation is freed, the allocator will check to see
if either span in front of or behind it is free, and if so
it will merge with either (or both) span(s).
}
HEAP_SIZE = 4096
HEAP_BEGIN = 8192 - HEAP_SIZE
HEAP_END = 8192 - 1
HEAP_PAGE_SIZE = 32
RXSTART = $0000
RXSTOP = (TXSTART - 2) | $0001 ' must be odd (B5 Errata)
' RXSTOP = (HEAP_BEGIN - 2) | $0001 ' must be odd (B5 Errata)
RXSIZE = (RXSTOP - RXSTART + 1)
{
PUB heap_init
' Write the leading byte
setSRAMWritePointer(HEAP_BEGIN)
wr_sram(constant(HEAP_SIZE/HEAP_PAGE_SIZE))
' Write the trailing byte
setSRAMWritePointer(HEAP_END)
wr_sram(constant(HEAP_SIZE/HEAP_PAGE_SIZE))
PUB heap_alloc(size) | iter,info
iter:=HEAP_BEGIN
size:=(size+2)/HEAP_PAGE_SIZE
repeat while itersize
' We need to split first!
' Write the leading byte
setSRAMWritePointer(iter+(size+1)*HEAP_PAGE_SIZE)
wr_sram(info-size-1)
' Write the trailing byte
setSRAMWritePointer(iter+info*HEAP_PAGE_SIZE+HEAP_PAGE_SIZE-1)
wr_sram(info-size-1)
info:=size
' Write the leading byte
setSRAMWritePointer(iter)
wr_sram(info|constant(1<<7))
' Write the trailing byte
setSRAMWritePointer(iter+info*HEAP_PAGE_SIZE+HEAP_PAGE_SIZE-1)
wr_sram(info|constant(1<<7))
return iter+1
iter+=((info&constant((1<<7)-1)+1)*HEAP_PAGE_SIZE)
' We couldn't find a suitable page!
abort -1
PUB heap_free(eptr)| previnfo,info,nextinfo,nextaddr
eptr--
setSRAMReadPointer(eptr-1)
previnfo:=rd_sram
info:=rd_sram & !(1<<7)
' Possibly merge with span before
if eptr<>HEAP_BEGIN
ifnot previnfo & constant(1<<7)
' Previous span is free, so lets merge these two
previnfo++
eptr-=previnfo*HEAP_PAGE_SIZE
info+=previnfo
' Possibly merge with span after
nextaddr:=eptr+info*HEAP_PAGE_SIZE+HEAP_PAGE_SIZE
if nextaddr -1
SynthFreq(xtalout, 25_000_000) 'determine ctr and frq for xtalout
pause.delay_ms(50)
init_ENC28J60
' write mac address to the chip
setMACAddress(macptr)
'heap_init
' check to make sure its a valid supported silicon rev
return hwVersion => silicon_rev
PUB stop
'' Stops the driver, frees 1 cog
spi_stop
PUB hwVersion
banksel(EREVID)
return rd_cntlreg(EREVID)
PUB setMACAddress(macptr)
' write mac address to the chip
banksel(MAADR1)
{
spi_out_cs(constant(cWCR | MAADR1))
repeat 5
spi_out_cs(byte[macptr++])
spi_out(byte[macptr++])
}
wr_reg(MAADR1,byte[macptr++])
wr_reg(MAADR2,byte[macptr++])
wr_reg(MAADR3,byte[macptr++])
wr_reg(MAADR4,byte[macptr++])
wr_reg(MAADR5,byte[macptr++])
wr_reg(MAADR6,byte[macptr++])
PUB rxPacketCount
banksel(EPKTCNT) ' re-select the packet count bank
return rd_cntlreg(EPKTCNT)
PUB isLinkUp
return rd_phy(PHSTAT2)&PHSTAT2_LSTAT <>0
PUB get_frame | new_rdptr
'' Get Ethernet Frame from Buffer
setSRAMReadPointer(ph_nextpacket)
rd_sram_block(@packetheader,6)
' protect from oversized packet
if ph_rxlen =< MAXFRAME
rd_sram_block(@packet,ph_rxlen)
new_rdptr := ph_nextpacket
' handle errata read pointer start (must be odd)
--new_rdptr
if (new_rdptr < RXSTART) OR (new_rdptr > RXSTOP)
new_rdptr := RXSTOP
bfs_reg(ECON2, ECON2_PKTDEC)
banksel(ERXRDPTL)
wr_reg_word(ERXRDPTL, new_rdptr)
PUB start_frame
'' Start frame - Inits the NIC and sets stuff
setSRAMWritePointer(TXSTART)
tx_end := constant(TXSTART - 1) ' start location is really address 0, so we are sending a count of - 1
wr_frame_byte(cTXCONTROL)
PUB wr_frame_byte(data)
spi_out_cs(cWBM)
spi_out(data)
++tx_end
PUB wr_frame_word(data)
spi_out_cs(cWBM)
spi_out_cs(byte[@data][1])
spi_out(byte[@data][0])
tx_end+=2
PUB wr_frame_long(data)
spi_out_cs(cWBM)
spi_out_cs(byte[@data][3])
spi_out_cs(byte[@data][2])
spi_out_cs(byte[@data][1])
spi_out(byte[@data][0])
tx_end+=4
PUB wr_frame_data(data,len) | i
wr_sram_block(data,len)
tx_end+=len
PUB wr_frame_pad(len)
spi_out_cs(cWBM)
repeat len-1
spi_out_cs(0)
spi_out(0)
tx_end+=len
PUB send_frame
'' Sends frame
'' Will retry on send failure up to 15 times with a 1ms delay in between repeats
repeat 15
if p_send_frame ' send packet, if successful then quit retry loop
quit
pause.delay_ms(1)
PUB calc_frame_ip_length : length
length:=tx_end-constant(TXSTART - 1)-14-1
setSRAMWritePointer(constant($10 + TXSTART +1))
wr_sram(length.byte[1])
wr_sram(length.byte[0])
PUB calc_frame_udp_length : length
length:=calc_frame_ip_length - 28
setSRAMWritePointer(constant($26 + TXSTART +1))
wr_sram(length.byte[1])
wr_sram(length.byte[0])
PUB calc_frame_ip_checksum
' TODO: This needs to be able to handle different header sizes!
return calc_checksum(14,constant(14+20),constant(14+20-10))
PUB calc_frame_icmp_checksum
return calc_checksum(34,tx_end-TXSTART, 36)
PUB calc_frame_tcp_checksum
'' For this to work, the partial checksum of the pseudo header needs
'' to be in the checksum field.
return calc_checksum(34,tx_end-TXSTART, $32)
PUB calc_frame_udp_checksum
'' For this to work, the partial checksum of the pseudo header needs
'' to be in the checksum field.
return calc_checksum(34,tx_end-TXSTART, 38)
PUB calc_checksum(crc_start, crc_end, dest) | econval, crc
crc_start += constant(TXSTART+1)
crc_end += TXSTART
banksel(EDMASTL)
wr_reg_word(EDMASTL, crc_start)
wr_reg_word(EDMANDL, crc_end)
' Wait for receive to finish, errata 15
repeat while ((rd_cntlreg(ESTAT) & constant(ESTAT_RXBUSY)))
' Enable and start checksum calculation
bfs_reg(ECON1, ECON1_CSUMEN|ECON1_DMAST)
' Wait for the DMA op to finish
repeat while ((rd_cntlreg(ECON1) & constant(ECON1_DMAST)))
crc_end := dest + constant(TXSTART+1)
crc := rd_cntlreg(EDMACSL) + (rd_cntlreg(EDMACSH) << 8)
' Now we write out the checksum back to the device
wr_reg_word(EWRPTL, crc_end)
wr_sram(crc.byte[1])
wr_sram(crc.byte[0])
return 1
PUB get_packetpointer
'' Gets packet pointer (for external object access)
return @packet
{
PUB get_mac_pointer
'' Gets mac address pointer
return @eth_mac
}
PUB get_rxlen
'' Gets received packet length
return ph_rxlen - 4 ' knock off the 4 byte Frame Check Sequence CRC, not used anywhere outside of this driver (pg 31 datasheet)
PRI rd_macreg(address) : data
'' Read MAC Control Register
spi_out_cs(cRCR | address)
spi_out_cs(0) ' transmit dummy byte
data.byte[0] := spi_in ' get actual data
PRI rd_macreg_word(address) : data
'' Read MAC Control Register
spi_out_cs(cRCR | address)
spi_out_cs(0) ' transmit dummy byte
data.byte[0] := spi_in ' get actual data
spi_out_cs(cRCR | address+1)
spi_out_cs(0) ' transmit dummy byte
data.byte[1] := spi_in ' get actual data
PRI rd_cntlreg(address) : data
'' Read ETH Control Register
spi_out_cs(cRCR | address)
data.byte[0] := spi_in
PRI wr_reg(address, data)
'' Write MAC and ETH Control Register
spi_out_cs(cWCR | address)
spi_out(data)
PRI wr_reg_word(address, data)
'' Write MAC and ETH Control Register
spi_out_cs(cWCR | address++)
spi_out(data.byte[0])
spi_out_cs(cWCR | address)
'spi_out_cs(data.byte[0])
spi_out(data.byte[1])
PRI bfc_reg(address, data)
'' Clear Control Register Bits
spi_out_cs(cBFC | address)
spi_out(data)
PRI bfs_reg(address, data)
'' Set Control Register Bits
spi_out_cs(cBFS | address)
spi_out(data)
PRI soft_reset
'' Soft Reset ENC28J60
spi_out(cSC)
PRI banksel(register)
'' Select Control Register Bank
bfc_reg(ECON1, %0000_0011)
bfs_reg(ECON1, register.byte[1]) ' high byte
PRI rd_phy(register): retVal
'' Read ENC28J60 PHY Register
banksel(MIREGADR)
wr_reg(MIREGADR, register)
wr_reg(MICMD, MICMD_MIIRD)
banksel(MISTAT)
repeat while ((rd_macreg(MISTAT) & MISTAT_BUSY) > 0)
banksel(MIREGADR)
wr_reg(MICMD, $00)
retVal := rd_macreg_word(MIRDL)
PRI wr_phy(register, data)
'' Write ENC28J60 PHY Register
banksel(MIREGADR)
wr_reg(MIREGADR, register)
wr_reg_word(MIWRL,data)
banksel(MISTAT)
repeat while ((rd_macreg(MISTAT) & MISTAT_BUSY) > 0)
PRI setSRAMReadPointer(x)
banksel(ERDPTL)
wr_reg_word(ERDPTL, x)
PRI setSRAMWritePointer(x)
banksel(EWRPTL)
wr_reg_word(EWRPTL, x)
PRI rd_sram : data
'' Read ENC28J60 8k Buffer Memory
spi_out_cs(cRBM)
data := spi_in
PRI wr_sram(data)
'' Write ENC28J60 8k Buffer Memory
spi_out_cs(cWBM)
spi_out(data)
PRI rd_sram_block(data_ptr,size)
'repeat size
' byte[data_ptr++]:=rd_sram
blockread(data_ptr,size)
PRI wr_sram_block(data_ptr,size)
blockwrite(data_ptr, size)
PRI init_ENC28J60 | starttime,i
'' Init ENC28J60 Chip
starttime:=cnt
repeat
if cnt-starttime>clkfreq
' Timeout!
abort 0
i := rd_cntlreg(ESTAT)
while (i & $08) OR (!i & ESTAT_CLKRDY)
soft_reset
pause.delay_ms(5) ' reset delay
bfc_reg(ECON1, ECON1_RXEN) ' stop send / recv
bfc_reg(ECON1, ECON1_TXRTS)
bfs_reg(ECON2, ECON2_AUTOINC) ' enable auto increment of sram pointers (already default)
packetheader[nextpacket_low] := RXSTART
packetheader[nextpacket_high] := constant(RXSTART >> 8)
banksel(ERDPTL)
wr_reg_word(ERDPTL, RXSTART)
banksel(ERXSTL)
wr_reg_word(ERXSTL, RXSTART)
wr_reg_word(ERXRDPTL, RXSTOP)
wr_reg_word(ERXNDL, RXSTOP)
wr_reg_word(ETXSTL, TXSTART)
banksel(MACON1)
wr_reg(MACON1, constant(MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN))
wr_reg(MACON3, constant(MACON3_TXCRCEN | MACON3_PADCFG0 | MACON3_FRMLNEN))
' don't timeout transmissions on saturated media
wr_reg(MACON4, MACON4_DEFER)
' collisions occur at 63rd byte
wr_reg(MACLCON2, 63)
wr_reg(MAIPGL, $12)
wr_reg(MAIPGH, $0C)
wr_reg_word(MAMXFLL, MAXFRAME)
' back-to-back inter-packet gap time
' full duplex = 0x15 (9.6us)
' half duplex = 0x12 (9.6us)
wr_reg(MABBIPG, $12)
' half duplex
wr_phy(PHCON2, PHCON2_HDLDIS)
wr_phy(PHCON1, $0000)
' set LED options (led A = link, led B = tx/rx)
wr_phy(PHLCON, $0472) '$0472
' enable packet reception
bfs_reg(ECON1, ECON1_RXEN)
PRI p_send_frame | i, eirval
'' Sends the frame
banksel(ETXSTL)
wr_reg_word(ETXSTL, TXSTART)
banksel(ETXNDL)
wr_reg_word(ETXNDL, tx_end)
' B5 Errata #10 - Reset transmit logic before send
bfs_reg(ECON1, ECON1_TXRST)
bfc_reg(ECON1, ECON1_TXRST)
' B5 Errata #10 & #13: Reset interrupt error flags
bfc_reg(EIR, constant(EIR_TXERIF | EIR_TXIF))
' trigger send
bfs_reg(ECON1, ECON1_TXRTS)
' fix for transmit stalls (derived from errata B5 #13), watches TXIF and TXERIF bits
' also implements a ~3.75ms (15 * 250us) timeout if send fails (occurs on random packet collisions)
' btw: this took over 10 hours to fix due to the elusive undocumented bug
i := 0
repeat
eirval := rd_cntlreg(EIR)
if ((eirval & constant(EIR_TXERIF | EIR_TXIF)) > 0)
quit
if (++i => 15)
eirval := EIR_TXERIF
quit
pause.delay_us(250)
' B5 Errata #13 - Reset TXRTS if failed send then reset logic
bfc_reg(ECON1, ECON1_TXRTS)
return ((eirval & EIR_TXERIF) == 0)
PRI SynthFreq(Pin, Freq) | s, d, ctr, frq
Freq := Freq #> 0 <# 128_000_000 'limit frequency range
if Freq < 500_000 'if 0 to 499_999 Hz,
ctr := constant(%00100 << 26) '..set NCO mode
s := 1 '..shift = 1
else 'if 500_000 to 128_000_000 Hz,
ctr := constant(%00010 << 26) '..set PLL mode
d := >|((Freq - 1) / 1_000_000) 'determine PLLDIV
s := 4 - d 'determine shift
ctr |= d << 23 'set PLLDIV
frq := fraction(Freq, CLKFREQ, s) 'Compute FRQA/FRQB value
ctr |= Pin 'set PINA to complete CTRA/CTRB value
CTRA := ctr 'set CTRA
FRQA := frq 'set FRQA
DIRA[Pin]~~ 'make pin output
PRI fraction(a, b, shift) : f
if shift > 0 'if shift, pre-shift a or b left
a <<= shift 'to maintain significant bits while
if shift < 0 'insuring proper result
b <<= -shift
repeat 32 'perform long division of a/b
f <<= 1
if a => b
a -= b
f++
a <<= 1
' ***************************************
' ** ASM SPI Engine **
' ***************************************
DAT
cog long 0
command long 0
CON
SPIOUT = %00_0001
SPIIN = %00_0010
SRAMWRITE = %00_0100
SRAMREAD = %00_1000
CSON = %01_0000
CSOFF = %10_0000
SPIBITS = 8
PRI spi_out(value)
setcommand(constant(SPIOUT | CSON | CSOFF), @value)
PRI spi_out_cs(value)
setcommand(constant(SPIOUT | CSON), @value)
PRI spi_in : value
setcommand(constant(SPIIN | CSON | CSOFF), @value)
PRI spi_in_cs : value
setcommand(constant(SPIIN | CSON), @value)
PRI blockwrite(startaddr, count)
setcommand(SRAMWRITE, @startaddr)
PRI blockread(startaddr, count)
setcommand(SRAMREAD, @startaddr)
PRI spi_start(_cs, _sck, _di, _do)
spi_stop
cspin := |< _cs
dipin := |< _di
dopin := |< _do
clkpin := |< _sck
cog := cognew(@init, @command) + 1
PRI spi_stop
if cog
cogstop(cog~ - 1)
command~
PRI setcommand(cmd, argptr)
command := cmd << 16 + argptr 'write command and pointer
repeat while command 'wait for command to be cleared, signifying receipt
DAT
org
init
or dira, cspin 'pin directions
andn dira, dipin
or dira, dopin
or dira, clkpin
or outa, cspin 'turn off cs (bring it high)
andn outa, dopin ' PreSet DataPin LOW
andn outa, clkpin ' PreSet ClockPin LOW
loop wrlong zero,par 'zero command (tell spin we are done processing)
:subloop rdlong t1,par wz 'wait for command
if_z jmp #:subloop
mov addr, t1 'used for holding return addr to spin vars
rdlong arg0, t1 'arg0
add t1, #4
rdlong arg1, t1 'arg1
' wrlong zero,par 'zero command to signify command received
mov lkup, addr 'get the command var from spin
shr lkup, #16 'extract the cmd from the command var
test lkup, #CSON wz 'turn on cs
if_nz andn outa, cspin
test lkup, #SPIOUT wz 'spi out
if_nz call #spi_out_
test lkup, #SPIIN wz 'spi in
if_nz call #xspi_in_
test lkup, #SRAMWRITE wz 'sram block write
if_nz jmp #sram_write_
test lkup, #SRAMREAD wz 'sram block read
if_nz jmp #sram_read_
test lkup, #CSOFF wz 'cs off
if_nz or outa, cspin
jmp #loop ' no cmd found
spi_out_ 'SHIFTOUT Entry
mov t4, #SPIBITS ' Load number of data bits
mov t3, arg0 ' Load t3 with DataValue
rol t3, #(32-SPIBITS)
:sout_loop
rol t3, #1 wc
muxc outa, dopin ' Set DataBit HIGH or LOW
or outa, clkpin ' Set ClockPin HIGH
andn outa, clkpin ' Set ClockPin LOW
djnz t4, #:sout_loop ' Decrement t4 ; jump if not Zero
andn outa, dopin
spi_out__ret ret ' Go wait for next command
spi_in_ 'SHIFTIN Entry
mov t4, #SPIBITS ' Load number of data bits
' andn outa, clkpin ' PreSet ClockPin LOW
:sin_loop
test dipin, ina wc ' Read Data Bit into 'C' flag
or outa, clkpin ' Set ClockPin HIGH
rcl t3, #1 ' rotate "C" flag into return value
andn outa, clkpin ' Set ClockPin LOW
djnz t4, #:sin_loop ' Decrement t4 ; jump if not Zero
mov arg0, t3
spi_in__ret ret ' Go wait for next command
xspi_in_
call #spi_in_
wrbyte arg0, addr
xspi_in__ret ret
' SRAM Block Read/Write
sram_write_ ' block write (arg0=hub addr, arg1=count)
mov t1, arg0
mov t2, arg1
andn outa, cspin
mov arg0, #cWBM
call #spi_out_
:loop rdbyte arg0, t1
call #spi_out_
add t1, #1
djnz t2, #:loop
or outa, cspin
jmp #loop
sram_read_ ' block read (arg0=hub addr, arg1=count)
mov t1, arg0
mov t2, arg1
andn outa, cspin
mov arg0, #cRBM
call #spi_out_
:loop call #spi_in_
wrbyte arg0, t1
add t1, #1
djnz t2, #:loop
or outa, cspin
jmp #loop
zero long 0 'constants
'values filled by spin code before launching
cspin long 0 ' chip select pin
dipin long 0 ' data in pin (enc28j60 -> prop)
dopin long 0 ' data out pin (prop -> enc28j60)
clkpin long 0 ' clock pin (prop -> enc28j60)
'temp variables
t1 res 1 ' loop and cog shutdown
t2 res 1 ' loop and cog shutdown
t3 res 1 ' Used to hold DataValue SHIFTIN/SHIFTOUT
t4 res 1 ' Used to hold # of Bits
t5 res 1 ' Used for temporary data mask
addr res 1 ' Used to hold return address of first Argument passed
lkup res 1 ' Used to hold command lookup
'arguments passed to/from high-level Spin
arg0 res 1 ' bits / start address
arg1 res 1 ' value / count
CON
' ***************************************
' ** ENC28J60 Control Constants **
' ***************************************
' ENC28J60 opcodes (OR with 5bit address)
cWCR = %010 << 5 ' write control register command
cBFS = %100 << 5 ' bit field set command
cBFC = %101 << 5 ' bit field clear command
cRCR = %000 << 5 ' read control register command
cRBM = (%001 << 5) | $1A ' read buffer memory command
cWBM = (%011 << 5) | $1A ' write buffer memory command
cSC = (%111 << 5) | $1F ' system command
' This is used to trigger TX in the ENC28J60, it shouldn't change, but you never know...
cTXCONTROL = $0E
' Packet header format (tail of the receive packet in the ENC28J60 SRAM)
#0,nextpacket_low,nextpacket_high,rec_bytecnt_low,rec_bytecnt_high,rec_status_low,rec_status_high
' ***************************************
' ** ENC28J60 Register Defines **
' ***************************************
' Bank 0 registers --------
ERDPTL = $00
ERDPTH = $01
EWRPTL = $02
EWRPTH = $03
ETXSTL = $04
ETXSTH = $05
ETXNDL = $06
ETXNDH = $07
ERXSTL = $08
ERXSTH = $09
ERXNDL = $0A
ERXNDH = $0B
ERXRDPTL = $0C
ERXRDPTH = $0D
ERXWRPTL = $0E
ERXWRPTH = $0F
EDMASTL = $10
EDMASTH = $11
EDMANDL = $12
EDMANDH = $13
EDMADSTL = $14
EDMADSTH = $15
EDMACSL = $16
EDMACSH = $17
' = $18
' = $19
' r = $1A
EIE = $1B
EIR = $1C
ESTAT = $1D
ECON2 = $1E
ECON1 = $1F
' Bank 1 registers -----
EHT0 = $100
EHT1 = $101
EHT2 = $102
EHT3 = $103
EHT4 = $104
EHT5 = $105
EHT6 = $106
EHT7 = $107
EPMM0 = $108
EPMM1 = $109
EPMM2 = $10A
EPMM3 = $10B
EPMM4 = $10C
EPMM5 = $10D
EPMM6 = $10E
EPMM7 = $10F
EPMCSL = $110
EPMCSH = $111
' = $112
' = $113
EPMOL = $114
EPMOH = $115
EWOLIE = $116
EWOLIR = $117
ERXFCON = $118
EPKTCNT = $119
' r = $11A
' EIE = $11B
' EIR = $11C
' ESTAT = $11D
' ECON2 = $11E
' ECON1 = $11F
' Bank 2 registers -----
MACON1 = $200
MACON2 = $201
MACON3 = $202
MACON4 = $203
MABBIPG = $204
' = $205
MAIPGL = $206
MAIPGH = $207
MACLCON1 = $208
MACLCON2 = $209
MAMXFLL = $20A
MAMXFLH = $20B
' r = $20C
MAPHSUP = $20D
' r = $20E
' = $20F
' r = $210
MICON = $211
MICMD = $212
' = $213
MIREGADR = $214
' r = $215
MIWRL = $216
MIWRH = $217
MIRDL = $218
MIRDH = $219
' r = $21A
' EIE = $21B
' EIR = $21C
' ESTAT = $21D
' ECON2 = $21E
' ECON1 = $21F
' Bank 3 registers -----
MAADR5 = $300
MAADR6 = $301
MAADR3 = $302
MAADR4 = $303
MAADR1 = $304
MAADR2 = $305
{MAADR1 = $300
MAADR0 = $301
MAADR3 = $302
MAADR2 = $303
MAADR5 = $304
MAADR4 = $305}
EBSTSD = $306
EBSTCON = $307
EBSTCSL = $308
EBSTCSH = $309
MISTAT = $30A
' = $30B
' = $30C
' = $30D
' = $30E
' = $30F
' = $310
' = $311
EREVID = $312
' = $313
' = $314
ECOCON = $315
' EPHTST $316
EFLOCON = $317
EPAUSL = $318
EPAUSH = $319
' r = $31A
' EIE = $31B
' EIR = $31C
' ESTAT = $31D
' ECON2 = $31E
' ECON1 = $31F
{******************************************************************************
* PH Register Locations
******************************************************************************}
PHCON1 = $00
PHSTAT1 = $01
PHID1 = $02
PHID2 = $03
PHCON2 = $10
PHSTAT2 = $11
PHIE = $12
PHIR = $13
PHLCON = $14
{******************************************************************************
* Individual Register Bits
******************************************************************************}
' ETH/MAC/MII bits
' EIE bits ----------
EIE_INTIE = (1<<7)
EIE_PKTIE = (1<<6)
EIE_DMAIE = (1<<5)
EIE_LINKIE = (1<<4)
EIE_TXIE = (1<<3)
EIE_WOLIE = (1<<2)
EIE_TXERIE = (1<<1)
EIE_RXERIE = (1)
' EIR bits ----------
EIR_PKTIF = (1<<6)
EIR_DMAIF = (1<<5)
EIR_LINKIF = (1<<4)
EIR_TXIF = (1<<3)
EIR_WOLIF = (1<<2)
EIR_TXERIF = (1<<1)
EIR_RXERIF = (1)
' ESTAT bits ---------
ESTAT_INT = (1<<7)
ESTAT_LATECOL = (1<<4)
ESTAT_RXBUSY = (1<<2)
ESTAT_TXABRT = (1<<1)
ESTAT_CLKRDY = (1)
' ECON2 bits --------
ECON2_AUTOINC = (1<<7)
ECON2_PKTDEC = (1<<6)
ECON2_PWRSV = (1<<5)
ECON2_VRTP = (1<<4)
ECON2_VRPS = (1<<3)
' ECON1 bits --------
ECON1_TXRST = (1<<7)
ECON1_RXRST = (1<<6)
ECON1_DMAST = (1<<5)
ECON1_CSUMEN = (1<<4)
ECON1_TXRTS = (1<<3)
ECON1_RXEN = (1<<2)
ECON1_BSEL1 = (1<<1)
ECON1_BSEL0 = (1)
' EWOLIE bits -------
EWOLIE_UCWOLIE = (1<<7)
EWOLIE_AWOLIE = (1<<6)
EWOLIE_PMWOLIE = (1<<4)
EWOLIE_MPWOLIE = (1<<3)
EWOLIE_HTWOLIE = (1<<2)
EWOLIE_MCWOLIE = (1<<1)
EWOLIE_BCWOLIE = (1)
' EWOLIR bits -------
EWOLIR_UCWOLIF = (1<<7)
EWOLIR_AWOLIF = (1<<6)
EWOLIR_PMWOLIF = (1<<4)
EWOLIR_MPWOLIF = (1<<3)
EWOLIR_HTWOLIF = (1<<2)
EWOLIR_MCWOLIF = (1<<1)
EWOLIR_BCWOLIF = (1)
' ERXFCON bits ------
ERXFCON_UCEN = (1<<7)
ERXFCON_ANDOR = (1<<6)
ERXFCON_CRCEN = (1<<5)
ERXFCON_PMEN = (1<<4)
ERXFCON_MPEN = (1<<3)
ERXFCON_HTEN = (1<<2)
ERXFCON_MCEN = (1<<1)
ERXFCON_BCEN = (1)
' MACON1 bits --------
MACON1_LOOPBK = (1<<4)
MACON1_TXPAUS = (1<<3)
MACON1_RXPAUS = (1<<2)
MACON1_PASSALL = (1<<1)
MACON1_MARXEN = (1)
' MACON2 bits --------
MACON2_MARST = (1<<7)
MACON2_RNDRST = (1<<6)
MACON2_MARXRST = (1<<3)
MACON2_RFUNRST = (1<<2)
MACON2_MATXRST = (1<<1)
MACON2_TFUNRST = (1)
' MACON3 bits --------
MACON3_PADCFG2 = (1<<7)
MACON3_PADCFG1 = (1<<6)
MACON3_PADCFG0 = (1<<5)
MACON3_TXCRCEN = (1<<4)
MACON3_PHDRLEN = (1<<3)
MACON3_HFRMEN = (1<<2)
MACON3_FRMLNEN = (1<<1)
MACON3_FULDPX = (1)
' MACON4 bits --------
MACON4_DEFER = (1<<6)
MACON4_BPEN = (1<<5)
MACON4_NOBKOFF = (1<<4)
MACON4_LONGPRE = (1<<1)
MACON4_PUREPRE = (1)
' MAPHSUP bits ----
MAPHSUP_RSTRMII = (1<<3)
' MICON bits --------
MICON_RSTMII = (1<<7)
' MICMD bits ---------
MICMD_MIISCAN = (1<<1)
MICMD_MIIRD = (1)
' EBSTCON bits -----
EBSTCON_PSV2 = (1<<7)
EBSTCON_PSV1 = (1<<6)
EBSTCON_PSV0 = (1<<5)
EBSTCON_PSEL = (1<<4)
EBSTCON_TMSEL1 = (1<<3)
EBSTCON_TMSEL0 = (1<<2)
EBSTCON_TME = (1<<1)
EBSTCON_BISTST = (1)
' MISTAT bits --------
MISTAT_NVALID = (1<<2)
MISTAT_SCAN = (1<<1)
MISTAT_BUSY = (1)
' ECOCON bits -------
ECOCON_COCON2 = (1<<2)
ECOCON_COCON1 = (1<<1)
ECOCON_COCON0 = (1)
' EFLOCON bits -----
EFLOCON_FULDPXS = (1<<2)
EFLOCON_FCEN1 = (1<<1)
EFLOCON_FCEN0 = (1)
' PHY bits
' PHCON1 bits ----------
PHCON1_PRST = (1<<15)
PHCON1_PLOOPBK = (1<<14)
PHCON1_PPWRSV = (1<<11)
PHCON1_PDPXMD = (1<<8)
' PHSTAT1 bits --------
PHSTAT1_PFDPX = (1<<12)
PHSTAT1_PHDPX = (1<<11)
PHSTAT1_LLSTAT = (1<<2)
PHSTAT1_JBSTAT = (1<<1)
' PHID2 bits --------
PHID2_PID24 = (1<<15)
PHID2_PID23 = (1<<14)
PHID2_PID22 = (1<<13)
PHID2_PID21 = (1<<12)
PHID2_PID20 = (1<<11)
PHID2_PID19 = (1<<10)
PHID2_PPN5 = (1<<9)
PHID2_PPN4 = (1<<8)
PHID2_PPN3 = (1<<7)
PHID2_PPN2 = (1<<6)
PHID2_PPN1 = (1<<5)
PHID2_PPN0 = (1<<4)
PHID2_PREV3 = (1<<3)
PHID2_PREV2 = (1<<2)
PHID2_PREV1 = (1<<1)
PHID2_PREV0 = (1)
' PHCON2 bits ----------
PHCON2_FRCLNK = (1<<14)
PHCON2_TXDIS = (1<<13)
PHCON2_JABBER = (1<<10)
PHCON2_HDLDIS = (1<<8)
' PHSTAT2 bits --------
PHSTAT2_TXSTAT = (1<<13)
PHSTAT2_RXSTAT = (1<<12)
PHSTAT2_COLSTAT = (1<<11)
PHSTAT2_LSTAT = (1<<10)
PHSTAT2_DPXSTAT = (1<<9)
PHSTAT2_PLRITY = (1<<5)
' PHIE bits -----------
PHIE_PLNKIE = (1<<4)
PHIE_PGEIE = (1<<1)
' PHIR bits -----------
PHIR_PLNKIF = (1<<4)
PHIR_PGIF = (1<<2)
' PHLCON bits -------
PHLCON_LACFG3 = (1<<11)
PHLCON_LACFG2 = (1<<10)
PHLCON_LACFG1 = (1<<9)
PHLCON_LACFG0 = (1<<8)
PHLCON_LBCFG3 = (1<<7)
PHLCON_LBCFG2 = (1<<6)
PHLCON_LBCFG1 = (1<<5)
PHLCON_LBCFG0 = (1<<4)
PHLCON_LFRQ1 = (1<<3)
PHLCON_LFRQ0 = (1<<2)
PHLCON_STRCH = (1<<1)