1042 lines
29 KiB
Plaintext
1042 lines
29 KiB
Plaintext
|
{{
|
||
|
ENC28J60 Ethernet MAC / PHY Driver
|
||
|
----------------------------------
|
||
|
|
||
|
Copyright (c) 2006-2009 Harrison Pham <harrison@harrisonpham.com>
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
of this software and associated documentation files (the "Software"), to deal
|
||
|
in the Software without restriction, including without limitation the rights
|
||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
copies of the Software, and to permit persons to whom the Software is
|
||
|
furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in
|
||
|
all copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
THE SOFTWARE.
|
||
|
|
||
|
The latest version of this software can be obtained from
|
||
|
http://hdpham.com/PropTCP and http://obex.parallax.com/
|
||
|
|
||
|
Constant Names / Code Logic based on code from
|
||
|
Microchip Technology, Inc.'s enc28j60.c / enc28j60.h source files
|
||
|
}}
|
||
|
|
||
|
CON
|
||
|
version = 6 ' major version
|
||
|
release = 0 ' minor version
|
||
|
|
||
|
CON
|
||
|
' ***************************************
|
||
|
' ** ENC28J60 SRAM Defines **
|
||
|
' ***************************************
|
||
|
' ENC28J60 Frequency
|
||
|
enc_freq = 25_000_000
|
||
|
|
||
|
' 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)
|
||
|
RXSTART = $0000
|
||
|
RXSTOP = (TXSTART - 2) | $0001 ' must be odd (B5 Errata)
|
||
|
RXSIZE = (RXSTOP - RXSTART + 1)
|
||
|
|
||
|
DAT
|
||
|
' ***************************************
|
||
|
' ** MAC Address Vars / Defaults **
|
||
|
' ***************************************
|
||
|
' ** This is the default MAC address used by this driver. The parent object
|
||
|
' can override this by passing a pointer to a new MAC address in the public
|
||
|
' start() method. It is recommend that this is done to provide a level of
|
||
|
' abstraction and makes tcp stack design easier.
|
||
|
' ** This is the ethernet MAC address, it is critical that you change this
|
||
|
' if you have more than one device using this code on a local network.
|
||
|
' ** If you plan on commercial deployment, you must purchase MAC address
|
||
|
' groups from IEEE or some other standards organization.
|
||
|
eth_mac byte $02, $00, $00, $00, $00, $01
|
||
|
|
||
|
' ***************************************
|
||
|
' ** Global Variables **
|
||
|
' ***************************************
|
||
|
rxlen word 0
|
||
|
tx_end word 0
|
||
|
|
||
|
packetheader byte 0[6]
|
||
|
|
||
|
'packet byte 0[MAXFRAME]
|
||
|
|
||
|
PUB start(_cs, _sck, _si, _so, xtalout, macptr)
|
||
|
'' Starts the driver (uses 1 cog for spi engine)
|
||
|
|
||
|
' Since some people don't have 25mhz crystals, we use the cog counters
|
||
|
' to generate a 25mhz frequency for the ENC28J60 (I love the Propeller)
|
||
|
' Note: This requires a main crystal that is a multiple of 25mhz (5mhz works).
|
||
|
spi_start(_cs, _sck, _so, _si, xtalout)
|
||
|
|
||
|
' If a MAC address pointer is provided (addr > -1) then copy it into
|
||
|
' the MAC address array (this kind of wastes space, but simplifies usage).
|
||
|
if macptr > -1
|
||
|
bytemove(@eth_mac, macptr, 6)
|
||
|
|
||
|
delay_ms(50)
|
||
|
init_ENC28J60
|
||
|
|
||
|
' return the chip silicon version
|
||
|
banksel(EREVID)
|
||
|
return rd_cntlreg(EREVID)
|
||
|
|
||
|
PUB stop
|
||
|
'' Stops the driver, frees 1 cog
|
||
|
|
||
|
spi_stop
|
||
|
|
||
|
PUB rd_macreg(address) : data
|
||
|
'' Read MAC Control Register
|
||
|
|
||
|
spi_out_cs(cRCR | address)
|
||
|
spi_out_cs(0) ' transmit dummy byte
|
||
|
data := spi_in ' get actual data
|
||
|
|
||
|
PUB rd_cntlreg(address) : data
|
||
|
'' Read ETH Control Register
|
||
|
|
||
|
spi_out_cs(cRCR | address)
|
||
|
data := spi_in
|
||
|
|
||
|
PUB wr_reg(address, data)
|
||
|
'' Write MAC and ETH Control Register
|
||
|
|
||
|
spi_out_cs(cWCR | address)
|
||
|
spi_out(data)
|
||
|
|
||
|
PUB bfc_reg(address, data)
|
||
|
'' Clear Control Register Bits
|
||
|
|
||
|
spi_out_cs(cBFC | address)
|
||
|
spi_out(data)
|
||
|
|
||
|
PUB bfs_reg(address, data)
|
||
|
'' Set Control Register Bits
|
||
|
|
||
|
spi_out_cs(cBFS | address)
|
||
|
spi_out(data)
|
||
|
|
||
|
PUB soft_reset
|
||
|
'' Soft Reset ENC28J60
|
||
|
|
||
|
spi_out(cSC)
|
||
|
|
||
|
PUB banksel(register)
|
||
|
'' Select Control Register Bank
|
||
|
|
||
|
bfc_reg(ECON1, %0000_0011)
|
||
|
bfs_reg(ECON1, register >> 8) ' high byte
|
||
|
|
||
|
PUB rd_phy(register) | low, high
|
||
|
'' 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)
|
||
|
low := rd_macreg(MIRDL)
|
||
|
high := rd_macreg(MIRDH)
|
||
|
return (high << 8) + low
|
||
|
|
||
|
PUB wr_phy(register, data)
|
||
|
'' Write ENC28J60 PHY Register
|
||
|
|
||
|
banksel(MIREGADR)
|
||
|
wr_reg(MIREGADR, register)
|
||
|
wr_reg(MIWRL, data)
|
||
|
wr_reg(MIWRH, data >> 8)
|
||
|
banksel(MISTAT)
|
||
|
repeat while ((rd_macreg(MISTAT) & MISTAT_BUSY) > 0)
|
||
|
|
||
|
PUB rd_sram : data
|
||
|
'' Read ENC28J60 8k Buffer Memory
|
||
|
|
||
|
spi_out_cs(cRBM)
|
||
|
data := spi_in
|
||
|
|
||
|
PUB wr_sram(data)
|
||
|
'' Write ENC28J60 8k Buffer Memory
|
||
|
|
||
|
spi_out_cs(cWBM)
|
||
|
spi_out(data)
|
||
|
|
||
|
PUB init_ENC28J60 | i
|
||
|
'' Init ENC28J60 Chip
|
||
|
|
||
|
repeat
|
||
|
i := rd_cntlreg(ESTAT)
|
||
|
while (i & $08) OR (!i & ESTAT_CLKRDY)
|
||
|
|
||
|
soft_reset
|
||
|
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(ERDPTL, RXSTART)
|
||
|
wr_reg(ERDPTH, constant(RXSTART >> 8))
|
||
|
|
||
|
banksel(ERXSTL)
|
||
|
wr_reg(ERXSTL, RXSTART)
|
||
|
wr_reg(ERXSTH, constant(RXSTART >> 8))
|
||
|
wr_reg(ERXRDPTL, RXSTOP)
|
||
|
wr_reg(ERXRDPTH, constant(RXSTOP >> 8))
|
||
|
wr_reg(ERXNDL, RXSTOP)
|
||
|
wr_reg(ERXNDH, constant(RXSTOP >> 8))
|
||
|
wr_reg(ETXSTL, TXSTART)
|
||
|
wr_reg(ETXSTH, constant(TXSTART >> 8))
|
||
|
|
||
|
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(MAMXFLL, MAXFRAME)
|
||
|
wr_reg(MAMXFLH, constant(MAXFRAME >> 8))
|
||
|
|
||
|
' back-to-back inter-packet gap time
|
||
|
' full duplex = 0x15 (9.6us)
|
||
|
' half duplex = 0x12 (9.6us)
|
||
|
wr_reg(MABBIPG, $12)
|
||
|
wr_reg(MAIPGL, $12)
|
||
|
wr_reg(MAIPGH, $0C)
|
||
|
|
||
|
' write mac address to the chip
|
||
|
banksel(MAADR1)
|
||
|
wr_reg(MAADR1, eth_mac[0])
|
||
|
wr_reg(MAADR2, eth_mac[1])
|
||
|
wr_reg(MAADR3, eth_mac[2])
|
||
|
wr_reg(MAADR4, eth_mac[3])
|
||
|
wr_reg(MAADR5, eth_mac[4])
|
||
|
wr_reg(MAADR6, eth_mac[5])
|
||
|
|
||
|
' half duplex
|
||
|
wr_phy(PHCON2, PHCON2_HDLDIS)
|
||
|
wr_phy(PHCON1, $0000)
|
||
|
|
||
|
' set LED options
|
||
|
wr_phy(PHLCON, $0742) ' $0472 => ledA = link, ledB = tx/rx
|
||
|
' $0742 => ledA = tx/rx, ledB = link
|
||
|
|
||
|
' enable packet reception
|
||
|
bfs_reg(ECON1, ECON1_RXEN)
|
||
|
|
||
|
PUB get_frame(pktptr) | packet_addr, new_rdptr
|
||
|
'' Get Ethernet Frame from Buffer
|
||
|
|
||
|
banksel(ERDPTL)
|
||
|
wr_reg(ERDPTL, packetheader[nextpacket_low])
|
||
|
wr_reg(ERDPTH, packetheader[nextpacket_high])
|
||
|
|
||
|
repeat packet_addr from 0 to 5
|
||
|
packetheader[packet_addr] := rd_sram
|
||
|
|
||
|
rxlen := (packetheader[rec_bytecnt_high] << 8) + packetheader[rec_bytecnt_low]
|
||
|
|
||
|
'bytefill(@packet, 0, MAXFRAME) ' Uncomment this if you want to clean out the buffer first
|
||
|
' otherwise, leave commented since it's faster to just leave stuff
|
||
|
' in the buffer
|
||
|
|
||
|
' protect from oversized packet
|
||
|
if rxlen =< MAXFRAME
|
||
|
rd_block(pktptr, rxlen)
|
||
|
{repeat packet_addr from 0 to rxlen - 1
|
||
|
BYTE[@packet][packet_addr] := rd_sram}
|
||
|
|
||
|
new_rdptr := (packetheader[nextpacket_high] << 8) + packetheader[nextpacket_low]
|
||
|
|
||
|
' 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(ERXRDPTL, new_rdptr)
|
||
|
wr_reg(ERXRDPTH, new_rdptr >> 8)
|
||
|
|
||
|
PUB start_frame
|
||
|
'' Start frame - Inits the NIC and sets stuff
|
||
|
|
||
|
banksel(EWRPTL)
|
||
|
wr_reg(EWRPTL, TXSTART)
|
||
|
wr_reg(EWRPTH, constant(TXSTART >> 8))
|
||
|
|
||
|
tx_end := constant(TXSTART - 1) ' start location is really address 0, so we are sending a count of - 1
|
||
|
|
||
|
wr_frame(cTXCONTROL)
|
||
|
|
||
|
PUB wr_frame(data)
|
||
|
'' Write frame data
|
||
|
|
||
|
wr_sram(data)
|
||
|
++tx_end
|
||
|
|
||
|
PUB wr_block(startaddr, count)
|
||
|
blockwrite(startaddr, count)
|
||
|
tx_end += count
|
||
|
|
||
|
PUB rd_block(startaddr, count)
|
||
|
blockread(startaddr, count)
|
||
|
|
||
|
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
|
||
|
delay_ms(1)
|
||
|
|
||
|
PRI p_send_frame | i, eirval
|
||
|
' Sends the frame
|
||
|
banksel(ETXSTL)
|
||
|
wr_reg(ETXSTL, TXSTART)
|
||
|
wr_reg(ETXSTH, constant(TXSTART >> 8))
|
||
|
|
||
|
banksel(ETXNDL)
|
||
|
wr_reg(ETXNDL, tx_end)
|
||
|
wr_reg(ETXNDH, tx_end >> 8)
|
||
|
|
||
|
' 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
|
||
|
delay_us(250)
|
||
|
|
||
|
' B5 Errata #13 - Reset TXRTS if failed send then reset logic
|
||
|
bfc_reg(ECON1, ECON1_TXRTS)
|
||
|
|
||
|
if ((eirval & EIR_TXERIF) == 0)
|
||
|
return true ' successful send (no error interrupt)
|
||
|
else
|
||
|
return false ' failed send (error interrupt)
|
||
|
|
||
|
PUB get_mac_pointer
|
||
|
'' Gets mac address pointer
|
||
|
return @eth_mac
|
||
|
|
||
|
PUB get_rxlen
|
||
|
'' Gets received packet length
|
||
|
return rxlen - 4 ' knock off the 4 byte Frame Check Sequence CRC, not used anywhere outside of this driver (pg 31 datasheet)
|
||
|
|
||
|
PRI delay_us(Duration)
|
||
|
waitcnt(((clkfreq / 1_000_000 * Duration - 3928)) + cnt)
|
||
|
|
||
|
PRI delay_ms(Duration)
|
||
|
waitcnt(((clkfreq / 1_000 * Duration - 3932)) + cnt)
|
||
|
|
||
|
' ***************************************
|
||
|
' ** ASM SPI Engine **
|
||
|
' ***************************************
|
||
|
DAT
|
||
|
cog long 0
|
||
|
command long 0
|
||
|
|
||
|
CON
|
||
|
SPIOUT = %0000_0001
|
||
|
SPIIN = %0000_0010
|
||
|
SRAMWRITE = %0000_0100
|
||
|
SRAMREAD = %0000_1000
|
||
|
CSON = %0001_0000
|
||
|
CSOFF = %0010_0000
|
||
|
CKSUM = %0100_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)
|
||
|
|
||
|
PUB chksum_add(startaddr, count)
|
||
|
setcommand(CKSUM, @startaddr)
|
||
|
return startaddr
|
||
|
|
||
|
PRI spi_start(_cs, _sck, _di, _do, _freqpin)
|
||
|
spi_stop
|
||
|
|
||
|
cspin := |< _cs
|
||
|
dipin := |< _di
|
||
|
dopin := |< _do
|
||
|
clkpin := |< _sck
|
||
|
|
||
|
ctramode := %0_00100_00_0000_0000_0000_0000_0000_0000 + _sck
|
||
|
ctrbmode := %0_00100_00_0000_0000_0000_0000_0000_0000 + _do
|
||
|
|
||
|
spi_setupfreqsynth(_freqpin)
|
||
|
|
||
|
cog := cognew(@init, @command) + 1
|
||
|
|
||
|
PRI spi_stop
|
||
|
if cog
|
||
|
cogstop(cog~ - 1)
|
||
|
ctra := 0
|
||
|
command~
|
||
|
|
||
|
PRI setcommand(cmd, argptr)
|
||
|
command := cmd << 16 + argptr 'write command and pointer
|
||
|
repeat while command 'wait for command to be cleared, signifying receipt
|
||
|
|
||
|
PRI spi_setupfreqsynth(pin)
|
||
|
|
||
|
if pin < 0
|
||
|
' pin num was negative -> disable freq synth
|
||
|
return
|
||
|
|
||
|
dira[pin] := 1
|
||
|
|
||
|
ctra := constant(%00010 << 26) '..set PLL mode
|
||
|
ctra |= constant((>|((enc_freq - 1) / 1_000_000)) << 23) 'set PLLDIV
|
||
|
|
||
|
frqa := spi_fraction(enc_freq, CLKFREQ, constant(4 - (>|((enc_freq - 1) / 1_000_000)))) 'Compute FRQA/FRQB value
|
||
|
ctra |= pin 'set PINA to complete CTRA/CTRB value
|
||
|
|
||
|
PRI spi_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
|
||
|
|
||
|
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)
|
||
|
|
||
|
mov frqb, #0 'disable ctrb increment
|
||
|
mov ctrb, ctrbmode
|
||
|
|
||
|
|
||
|
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
|
||
|
|
||
|
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
|
||
|
|
||
|
test lkup, #CKSUM wz 'perform checksum
|
||
|
if_nz call #csum16
|
||
|
|
||
|
jmp #loop ' no cmd found
|
||
|
|
||
|
|
||
|
spi_out_ andn outa, clkpin
|
||
|
shl arg0, #24
|
||
|
mov phsb, arg0 ' data to write
|
||
|
mov frqa, freqw ' 20MHz write frequency
|
||
|
mov phsa, #0 ' start at clocking at 0
|
||
|
|
||
|
mov ctra, ctramode ' send data @ 20MHz
|
||
|
rol phsb, #1
|
||
|
rol phsb, #1
|
||
|
rol phsb, #1
|
||
|
rol phsb, #1
|
||
|
rol phsb, #1
|
||
|
rol phsb, #1
|
||
|
rol phsb, #1
|
||
|
mov ctra, #0 ' disable
|
||
|
andn outa, clkpin
|
||
|
|
||
|
spi_out__ret ret
|
||
|
|
||
|
|
||
|
spi_in_ andn outa, clkpin
|
||
|
mov phsa, phsr ' start phs for clock
|
||
|
mov frqa, freqr ' 10MHz read frequency
|
||
|
nop
|
||
|
|
||
|
mov ctra, ctramode ' start clocking
|
||
|
test dipin, ina wc
|
||
|
rcl arg0, #1
|
||
|
test dipin, ina wc
|
||
|
rcl arg0, #1
|
||
|
test dipin, ina wc
|
||
|
rcl arg0, #1
|
||
|
test dipin, ina wc
|
||
|
rcl arg0, #1
|
||
|
test dipin, ina wc
|
||
|
rcl arg0, #1
|
||
|
test dipin, ina wc
|
||
|
rcl arg0, #1
|
||
|
test dipin, ina wc
|
||
|
rcl arg0, #1
|
||
|
test dipin, ina wc
|
||
|
mov ctra, #0 ' stop clocking
|
||
|
rcl arg0, #1
|
||
|
andn outa, clkpin
|
||
|
|
||
|
spi_in__ret ret
|
||
|
|
||
|
xspi_in_ call #spi_in_
|
||
|
wrbyte arg0, addr ' write byte back to spin result var
|
||
|
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
|
||
|
|
||
|
csum16 ' performs checksum 16bit additions on the data
|
||
|
' arg0=hub addr, arg1=length, writes sum to first arg
|
||
|
mov t1, #0 ' clear sum
|
||
|
:loop rdbyte t2, arg0 ' read two bytes (16 bits)
|
||
|
add arg0, #1
|
||
|
rdbyte t3, arg0
|
||
|
add arg0, #1
|
||
|
shl t2, #8 ' build the word
|
||
|
add t2, t3
|
||
|
add t1, t2 ' add numbers
|
||
|
mov t2, t1 ' add lower and upper words together
|
||
|
shr t2, #16
|
||
|
and t1, hffff
|
||
|
add t1, t2
|
||
|
sub arg1, #2
|
||
|
cmp arg1, #1 wz, wc
|
||
|
if_nc_and_nz jmp #:loop
|
||
|
if_z rdbyte t2, arg0 ' add last byte (odd)
|
||
|
if_z shl t2, #8
|
||
|
if_z add t1, t2
|
||
|
wrlong t1, addr ' return result back to SPIN
|
||
|
csum16_ret ret
|
||
|
|
||
|
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)
|
||
|
ctramode long 0 ' ctr mode for CLK
|
||
|
ctrbmode long 0 ' ctr mode for SPI Out
|
||
|
|
||
|
hffff long $FFFF
|
||
|
|
||
|
freqr long $2000_0000 'frequency of SCK /8 for receive
|
||
|
freqw long $4000_0000 'frequency of SCK /4 for send
|
||
|
phsr long $6000_0000
|
||
|
|
||
|
'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)
|