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) |