Writing FCode 3.x Programs
只搜寻这本书
以 PDF 格式下载本书

Network Devices

9

This chapter describes how to implement network device drivers.
Network devices are packet-oriented devices capable of sending and receiving packets addressed according to IEEE 802.2 (Ethernet). OpenBoot firmware typically uses network devices for diskless booting. The standard /obp-tftp support package assists in the implementation of the load method for this device type.
Normally the network device driver would have a one level tree or a two level tree. The user can create a multi-level tree by applying new-device and finish-device.
A one level tree could have several nodes, depending on how many net channels the plug-in card can support, each node corresponds to one net channel.
This chapter shows three sample network device drivers for the Quad Ethernet device card. The device tree structure for the examples is as follows:
Each QED SBus card defines two levels:
  • one qec device node
  • four qe device nodes

图形

Figure 9-1

The general pathname (after sbus or sbi) for a qe node is

  qec@S,20000/qe@C,0  

where S is the SBus slot number and C is the network channel number.

Required Methods

The network device FCode must declare the network device-type, and must implement the methods open and close, as well as the following methods:
load ( addr -- len )
Read the default stand-alone program into memory starting at addr using the default network booting protocol. len is the size in bytes of the program read in.
read ( addr len -- actual )
Receive a network packet, placing at most the first len bytes in memory at addr. Return the actual number of bytes received (not the number copied), or -2 if no packet is currently available. Packets with hardware-detected errors are discarded as though they were not received. Do not wait for a packet (non-blocking).
write ( addr len -- actual )
Transmit the network packet of size len bytes starting at memory address addr. Return the number of bytes actually transmitted. The packet must be complete with all addressing information, including source hardware address.

Required Device Properties

The required properties for a network device are:
Table 9-1
NameTypical Value
name" SUNW,my-net"
reglist of registers {device-dependent}
device_type" network"
mac-address8 0 0x20 0x0c 0xea 0x41 {the MAC address currently being used.}

Optional Device Properties

Several other properties may be declared for network devices:
Table 9-2
Property NameTypical Property Value
max-frame-size0x4000
address-bits48
slave-burst-sizes0x7f {depends on the number of entries in the reg property}
local-mac-address8 0 0x20 0x0c 0xea 0x41 {the built-in Media Access Control address.}

Device Driver Examples

Simple Network Device Example

At minimum, a network device driver need only provide the desired tree structure and to publish all the necessary properties to identify the devices.
Code Example 7 QED Identification ROM Sample

  \ QED identification ROM  
  \ qed-idrom.fth  
  
  fcode-version 1  
  
     fload board.fth  
     headers  
     : copyright ( -- )  
        ." Two-level QED-IDROM 1.1 " cr  
        ." Copyright 1992 - 1995 Sun Microsystems, Inc..  All Rights Reserved" cr  
     ;  
  
     : identify-qed  ( -- )  
        create-qec-properties  
        4 0  do  
           new-device  
           i create-qe-properties  
           finish-device  
        loop  
     ;  
     identify-qed  
  
  end0  
  
  \ -----------------------------------------------------------------  
  \ board.fth  
  \ To define required properties for QED devices.  
  
     headers  
     my-address   constant my-sbus-addr  
     my-space     constant my-sbus-space  
     headerless  

Code Example 7 QED Identification ROM Sample (Continued)

  \ QED identification ROM  
  \ Define the address map.  
  \ MED Address Map PA[18:0] (totally 512KB address space).  
  \  h# 00.0000   constant eprom-pa  
  \  h# 00.8000   constant /eprom                 \ 32KB used, 64KB total  
     h# 01.0000   constant mace-regs-offset  
     h# 01.0000   constant mace0-base  
     h# 01.4000   constant mace1-base  
     h# 01.8000   constant mace2-base  
     h# 01.c000   constant mace3-base  
     h# 00.4000   constant /mace-regs             \ 16KB per channel, 64KB total  
     h# 02.0000   constant global-regs-offset  
     h# 01.0000   constant /global-regs           \ 64KB total  
     h# 03.0000   constant channel-regs-offset  
     h# 03.0000   constant channel0-base  
     h# 03.4000   constant channel1-base  
     h# 03.8000   constant channel2-base  
     h# 03.c000   constant channel3-base  
     h# 00.4000   constant /channel-regs          \ 16KB per channel, 64KB total  
     h# 04.0000   constant locmem-pa  
     h# 01.0000   constant /locmem                \ 64KB used, 256KB total  
  
  \ Real size of mace/qec-global/qec-channel registers.  
     20 constant /qec-mace-regs  
     14 constant /qec-global-regs  
     34 constant /qec-channel-regs  
  
  \ Miscellaneous constant definitions.  
     1            constant #channels  
     h# 4000      constant max-frame-size         ( d# 1536 for le )  
     d# 48        constant address-bits  
  \ Hardwired SBus interrupt level for MED.  
     4  constant sbus-qe-intr  
  
     : encode-reg  ( addr space size -- addr len ) >r encode-phys  r> encode-int encode+ ;  
  
     : encode-ranges  ( offs bustype  phys offset size -- addr len )  
        >r >r >r  encode-phys  r> r> r> encode-reg  encode+  
     ;  
     : offset>physical-addr  ( offset -- paddr.lo paddr.hi )  
        my-sbus-addr + my-sbus-space  
     ;  
  
  headers  

Code Example 7 QED Identification ROM Sample (Continued)

  \ QED identification ROM  
     : create-qec-properties  ( -- )  
        " qec"      name  
        " SUNW,595-3198" encode-string " model" property     \ 595-3198-01  
        global-regs-offset offset>physical-addr /global-regs encode-reg  
        locmem-pa  offset>physical-addr /locmem encode-reg encode+  
        " reg" property  
  
        0 0 channel0-base  offset>physical-addr /channel-regs encode-ranges  
        0 1 channel1-base  offset>physical-addr /channel-regs encode-ranges encode+  
        0 2 channel2-base  offset>physical-addr /channel-regs encode-ranges encode+  
        0 3 channel3-base  offset>physical-addr /channel-regs encode-ranges encode+  
        0 h# 10 mace0-base offset>physical-addr /mace-regs    encode-ranges encode+  
        0 h# 11 mace1-base offset>physical-addr /mace-regs    encode-ranges encode+  
        0 h# 12 mace2-base offset>physical-addr /mace-regs    encode-ranges encode+  
        0 h# 13 mace3-base offset>physical-addr /mace-regs    encode-ranges encode+  
        " ranges" property  
  
        #channels encode-int " #channels" property  
      \ One interrupt per qec, not one interrupt per channel.  
        sbus-qe-intr  encode-int  " interrupts"  property  
     ;  
     : create-qe-properties  ( chan# -- )  
        >r  
        " qe" encode-string  " name"   property  
        r@ encode-int " channel#" property  
        max-frame-size encode-int " max-frame-size" property  
        address-bits encode-int " address-bits" property  
        0 r@ /channel-regs encode-reg  
          0 r@ h# 10 + /mace-regs encode-reg encode+  
          " reg" property  
        r> drop  
     ;  

Sample Driver With Test and Debugging Methods

This version of a network device driver is still non-bootable, but it shows how an intermediate step of driver can be used to debug and test the device during or after development.
The coding techniques shown in this and the following examples are:
  • Each qe node has the same set of instance variables as each of the other qe nodes.
  • All the qe nodes use the same qe driver source code defined in the first qe node (qe0).
Code Example 8 QED Test ROM Sample

  \ QED test ROM.  
  \ qed-test.fth  
  
  fcode-version2  
     headers  
     fload board.fth  
     : copyright ( -- )  
        ." QED-TEST 1.1 " cr  
        ." Copyright 1992 - 1995 Sun Microsystems, Inc..  All Rights Reserved" cr  
     ;  
  : instance ( -- ) fcode-revision 20001 >= if instance then;  
  \ Create qec device node.  
     create-qec-properties  
     fload qec-test.fth   \ qec test code.  
  
  \ Create qe0 device node.  
     new-device  
        0 create-qe-properties  
        : dma-sync  ( virt-addr dev-addr size -- )  " dma-sync" $call-parent  ;  
  
        \ ***** qe0 instance variables *****  
        0 instance value mace   \ virtual address of Mace registers base  
        0 instance value qecc   \ virtual address of Qec channel registers base  
        instance variable my-channel# \ qe channel#  
        my-channel# off  
        fload qe-test.fth       \ qe test code.  
  
        \ ***** qe0 external methods *****  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
        external  
        : selftest  ( -- fail? )  
           qe0-selftest  
        ;  
        : open  ( -- okay? )  
           qe0-open  
        ;  
        : close  ( -- )  
           qe0-close  
        ;  
        : reset  ( -- )  
           qe0-reset  
        ;  
        headers  
     finish-device  
  
  \ Create qe1 device node.  
     new-device  
        1 create-qe-properties  
  
        \ ***** qe1 instance variables *****  
        0 instance value mace   \ virtual address of Mace registers base  
        0 instance value qecc   \ virtual address of Qec channel registers base  
        instance variable my-channel# \ qe channel#  
        my-channel# off  
  
        \ ***** qe1 external methods *****  
        external  
        : selftest  ( -- fail? )  
           qe0-selftest  
        ;  
        : open  ( -- okay? )  
           qe0-open  
        ;  
        : close  ( -- )  
           qe0-close  
        ;  
        : reset  ( -- )  
           qe0-reset  
        ;  
        headers  
     finish-device  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
  \ Create qe2 device node.  
     new-device  
        2 create-qe-properties  
  
        \ ***** qe2 instance variables *****  
        0 instance value mace   \ virtual address of Mace registers base  
        0 instance value qecc   \ virtual address of Qec channel registers base  
        instance variable my-channel# \ qe channel#  
        my-channel# off  
  
        \ ***** qe2 external methods *****  
        external  
        : selftest  ( -- fail? )  
           qe0-selftest  
        ;  
        : open  ( -- okay? )  
           qe0-open  
        ;  
        : close  ( -- )  
           qe0-close  
        ;  
        : reset  ( -- )  
           qe0-reset  
        ;  
        headers  
     finish-device  
  
  \ Create qe3 device node.  
     new-device  
        3 create-qe-properties  
  
        \ ***** qe3 instance variables *****  
        0 instance value mace   \ virtual address of Mace registers base  
        0 instance value qecc   \ virtual address of Qec channel registers base  
        instance variable my-channel# \ qe channel#  
        my-channel# off  
  
        \ ***** qe3 external methods *****  
        external  
        : selftest  ( -- fail? )  
           qe0-selftest  
        ;  
        : open  ( -- okay? )  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
           qe0-open  
        ;  
        : close  ( -- )  
           qe0-close  
        ;  
        : reset  ( -- )  
           qe0-reset  
        ;  
        headers  
     finish-device  
  
  end0  
  
  \ ---------------------------------------------------------------------  
  \ qec-test.fth  
  \ Test code for the qec node.  
  
  /locmem #channels / value chmem  
  chmem 2/ value rxbufsize  
  
  \ ***** qed utility (from qed-util.fth) *****  
  
  : lwrt-rd-cmp ( mask data addr -- success? )  
     2dup rl! rl@ rot and =  
  ;  
  : cwrt-rd-cmp ( mask data addr -- success? )  
     2dup rb! rb@ rot and =  
  ;  
  instance defer wrt-rd-cmp  
  ' lwrt-rd-cmp to wrt-rd-cmp  
  d# 32 instance value #bits  
  
  external  
  : wlk-test ( mask addr #bits -- success? )  
     dup to #bits  
     d# 32 =  if  ['] lwrt-rd-cmp  else  ['] cwrt-rd-cmp  then  to wrt-rd-cmp  
     true -rot     ( true mask addr )  
     #bits 0  
     do      ( flag0 mask addr )  
        over 1 i lshift and ?dup  if                  ( flag0 mask addr data )  
           >r 2dup r> swap wrt-rd-cmp false =     ( flag0 mask addr flag )  
           if  rot drop false -rot leave  then  
        then  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
     loop  
     2drop  
  ;  
  
  headers  
  instance variable ms-timeout  
  
  external  
  : set-ms-timeout  ( #ms -- ) ms-timeout !  ;  
  : ms-timeout?  ( -- flag )  
     ms-timeout @ dup  if  
        1- ms-timeout ! 1 ms false  
     else  
        drop true  
     then  
  ;  
  headers  
  
  \ ***** qec global register (from global.h.fth) *****  
  \  
  \ QEC Global register set.  
  \  
  
  \ Virtual addresses of QEC global registers.  
  \ The actual addresses will be assigned later.  
  0 instance value qecg  
  
  hex  
  \ global control register (RW)  
  : qecg-control   ( -- vaddr )  qecg  ;  
  : qecg-control@  ( -- data )   qecg-control rl@  ;  
  : qecg-control!  ( data -- )   qecg-control rl!  ;  
  
  headerless  
  \ For Global Control Register.  
  f000.0000 constant gcr-mode     \ Mode mask  
  4000.0000 constant gcr-mace     \ Mace mode  
  1 constant gcr-reset    \ Reset bit (0), 1 to enable reset.  
  
  headers  
  
  \ ***** qec map (from qecmap.fth ) *****  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
  0 instance value locmem-base  
  false value dma-sync?  
  0 value dma-sync-addr  
  
  : find-dma-sync  ( -- )  
     " dma-sync" my-parent ihandle>phandle find-method  if  
        true to dma-sync?  
        to dma-sync-addr  
     then  
  ;  
  find-dma-sync  
  
  external  
  : decode-unit  ( addr len -- address space )  decode-2int  ;  
  : map-in  ( offset slot# #bytes -- virtual )  " map-in" $call-parent  ;  
  : map-out  ( addr len -- )  " map-out" $call-parent  ;  
  : dma-map-in  ( vaddr n cache? -- devaddr )  " dma-map-in" $call-parent  ;  
  : dma-map-out  ( vaddr devaddr n -- )  " dma-map-out" $call-parent  ;  
  : dma-alloc ( size -- addr )  " dma-alloc" $call-parent  ;  
  : dma-free ( addr size -- )  " dma-free" $call-parent  ;  
  
  \ Dma-sync could be dummy routine if parent device doesn't support.  
  \ sun4c ROMs may not support it.  
  : dma-sync  ( virt-addr dev-addr size -- )  
     dma-sync?  if  
        dma-sync-addr my-parent call-package  
     else  
        3drop  
     then  
  ;  
  
  headers  
  
  : map-qec-regs  ( -- )  
     global-regs-offset my-sbus-addr + my-sbus-space /qec-global-regs  
     " map-in" $call-parent to qecg  
  ;  
  : unmap-qec-regs  ( -- )  
     qecg /qec-global-regs " map-out" $call-parent  
     0 to qecg  
  ;  
  
  : map-locmem  ( -- )  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
     locmem-pa my-sbus-addr + my-sbus-space /locmem  
     " map-in" $call-parent to locmem-base  
  ;  
  : unmap-locmem  ( -- )  
     locmem-base /locmem " map-out" $call-parent  
     0 to locmem-base  
  ;  
  
  \ ***** qec test (from qectest.fth) *****  
  hex  
  
  headerless  
  \ 18 constant /qec-global-regs  
  \ Define the mask bits that can be tested for each global register.  
  create gl-reg-masks  
          0000.001e , 0000.0000 , 0000.0000 , 0001.e000 ,  
          0000.f000 , 0000.f000 ,  
  
  \ Test Qec global registers.  
  : gl-reg-test  ( -- success? )  
     true  
     /qec-global-regs 0  do                       ( flag0 )  
        gl-reg-masks i + @  
        qecg i + d# 32 wlk-test                   ( flag0 flag )  
        false =  if  drop false leave  then       ( flag0 )  
   /n +loop  
  ;  
  
  \ Perform register test for the qec node.  
  :  qec-reg-test ( -- success? )  
     diagnostic-mode?  if  
        ."  Qec register test -- "  
     then  
     gl-reg-test  
     diagnostic-mode?  if  
        dup  if  ." succeeded."  else  ." failed."  then  cr  
     then  
  ;  
  
  headers  
  
  \ ***** qec package *****  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
  
  : reset-qec-global  ( -- fail? )  
     gcr-reset qecg-control!           \ Issue global reset.  
     d# 100 set-ms-timeout  
     begin  
        qecg-control@ gcr-reset and  
     while  
        ms-timeout?  if ." Global reset failed" cr true exit then  
     repeat  
     false  
  ;  
  : identify-chip  ( -- okay? )  
     qecg-control@ gcr-mode and gcr-mace =  
  ;  
  
  external  
  : open  ( -- true )  
     map-qec-regs  
     identify-chip dup 0=  if  
        unmap-qec-regs  
     then  
  ;  
  : close ( -- )  
     qecg  if unmap-qec-regs then  
  ;  
  
  : selftest ( -- fail? )  
     qecg                 ( qecg )  
     map-qec-regs  
     qec-reg-test         ( qecg success? )  
     unmap-qec-regs  
     swap to qecg         ( success? )  
     0=                   ( fail? )  
  ;  
  
  : reset  ( -- )  
     qecg  
     map-qec-regs  
     reset-qec-global drop  
     unmap-qec-regs  
     to qecg  
  ;  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
  headers  
  
  \ ---------------------------------------------------------------------  
  \ qe-test.fth  
  \ Test code for the qe node.  
  
  : wlk-test  ( mask addr #bits -- success? )  " wlk-test" $call-parent  ;  
  : set-ms-timeout  ( #ms -- )  " set-ms-timeout" $call-parent  ;  
  : ms-timeout?  ( -- flag )  " ms-timeout?" $call-parent  ;  
  
  \ ***** qe map (from qemap.fth) *****  
  
  headers  
  \ instance variable my-channel# my-channel# off  
  : my-channel#!  ( channel# -- )  my-channel# !  ;  
  
  : my-chan#  ( -- channel# )  
     my-channel# @  
  ;  
  : mace-regs  ( -- devaddr space size )  
     my-sbus-addr mace-regs-offset + /mace-regs my-chan# * +  
     my-sbus-space /qec-mace-regs  
  ;  
  
  : map-mace    ( -- )  
     mace-regs " map-in" my-parent $call-method to mace  
  ;  
  : unmap-mace  ( -- )  
     mace /qec-mace-regs " map-out" my-parent $call-method  
     0 to mace  
  ;  
  
  : channel-regs  ( -- devaddr space size )  
     my-sbus-addr channel-regs-offset + /channel-regs my-chan# * +  
     my-sbus-space /qec-channel-regs  
  ;  
  
  : map-channel    ( -- )  
     channel-regs " map-in" my-parent $call-method to qecc  
  ;  
  : unmap-channel  ( -- )  
     qecc /qec-channel-regs " map-out" my-parent $call-method  
     0 to qecc  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
  ;  
  
  : map-chips  ( -- )  
     mace 0=  if          \  Do mapping if it is unmapped.  
        map-mace  
        map-channel  
     then  
  ;  
  : unmap-chips  ( -- )  
     mace  if             \ Do unmapping if it is mapped.  
        unmap-channel  
        unmap-mace  
     then  
  ;  
  
  \ ***** qe test (from qeregtst.fth) *****  
  
  hex  
  
  \ Define the mask bits that can be tested for each register.  
  create ch-reg-masks  
          0000.0004 , 0000.0000 , ffff.f800 , ffff.f800 ,  
          0000.0001 , 0000.0001 , 001f.001f , 1fc0.3fc0 ,  
          0000.fffe , 0000.fffe , 0000.fffe , 0000.fffe ,  
          0000.00ff ,  
  create mace-reg-masks  
          00 c, 00 c, 89 c, 00 c, 00 c, 0d c, 00 c, 00 c,  
          00 c, 67 c, 00 c, 70 c, f3 c, ef c, 04 c, 5f c,  
          00 c, 00 c, 00 c, 00 c, 00 c, 00 c, 00 c, 00 c,  
          00 c, 00 c, 00 c, 00 c, 00 c, 00 c, 00 c, 00 c,  
  
  \ Test Qec per channel registers.  
  : ch-reg-test  ( -- flag )  
     true  
     /qec-channel-regs 0 do                       ( flag0 )  
        ch-reg-masks i + @  
        qecc i + d# 32 wlk-test                   ( flag0 flag )  
        false =  if  drop false leave  then       ( flag0 )  
   /n +loop  
  ;  
  \ Test Mace registers.  
  : mace-reg-test  ( -- flag )  
     true  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
     /qec-mace-regs 0 do                          ( flag0 )  
        mace-reg-masks i + c@  
        mace i + 8 wlk-test                       ( flag0 flag )  
        false =  if  drop false leave  then       ( flag0 )  
     loop  
  ;  
  
  \ Perform register test for the qe node.  
  :  qe-reg-test ( -- success? )  
     diagnostic-mode?  if  
        ."  Qe register test -- "  
     then  
     ch-reg-test  
     mace-reg-test and  
     diagnostic-mode?  if  
        dup  if  ." succeeded."  else  ." failed."  then  cr  
     then  
  ;  
  
  \ ***** qe0 package *****  
  
  headerless  
  \ For MACE BIU Configuration Control (R11). (RW)  
  01 constant m-swrst             \ software reset  
  : mace-biucc  ( -- vaddr )  h# 0b mace +  ;  
  : mace-biucc@   ( -- data )  mace-biucc rb@  ;  
  : mace-biucc!   ( data -- )  mace-biucc rb!  ;  
  \ For QEC per channel control reg. (RW)  
  02 constant c-rst  
  : qecc-control  ( -- vaddr ) qecc  ;  
  : qecc-control@         ( -- data )  qecc-control rl@  ;  
  : qecc-control!         ( data -- )  qecc-control rl!  ;  
  
  headers  
  : set-my-channel#  ( -- )  
  \ If don't find the channel property, use 0.  
     " channel#" get-my-property  if  0  else  decode-int nip nip  then  
     my-channel#!  
  ;  
  \ Reset (or stop) the qec channel.  
  \       Issue a soft reset to the desired Mace.  
  \       Then issue a soft reset to the desired channel in QEC.  
  \ Chip reset algorithm:  

Code Example 8 QED Test ROM Sample (Continued)

  \ QED test ROM.  
  \       Set the reset bit then wait until the reset bit cleared.  
  \ Timeout in 0.1 sec if fail.  
  \  
  : channel-reset  ( -- fail? )  
     m-swrst mace-biucc!          \ Issue Mace reset.  
     d# 100 set-ms-timeout  
     begin  
        mace-biucc@ m-swrst and  
     while  
        ms-timeout? if ." Cannot reset Mace" cr true exit then  
     repeat  
     c-rst qecc-control!          \ Reset QEC channel registers.  
     d# 100 set-ms-timeout  
     begin  
        qecc-control@ c-rst and  
     while  
        ms-timeout?  if  ." Cannot reset QEC channel" cr true exit  then  
     repeat  
     false  
  ;  
  
  external  
  : qe0-selftest  ( -- flag )     \ Flag 0 if passes test.  
     set-my-channel#  
     map-chips  
     qe-reg-test          ( success? )  
     unmap-chips  
     0=                   ( fail? )  
  ;  
  
  : qe0-open  ( -- okay? )  
     set-my-channel#  
     mac-address drop 6 encode-string  " mac-address" property  
     true  
  ;  
  : qe0-close  ( -- )  
  ;  
  : qe0-reset  ( -- )  
     set-my-channel#  
     map-chips channel-reset drop unmap-chips  
  ;  
  headers  

Bootable Network Device Driver Example

The example below shows a complete version of a bootable network driver. It implements the selftest method callable by OpenBoot test commands and the watch-net method callable by OpenBoot watch-net and watch-net-all commands.
Code Example 9 QED Bootable Driver Sample

  \ QED bootable driver  
  \ qed.fth  
  
  fcode-version1  
     headers  
     fload board.fth  
     : copyright ( -- )  
        ." QED 1.1 " cr  
        ." Copyright 1992-1995 Sun Microsystems, Inc.  All Rights Reserved" cr  
     ;  
     : instance ( -- ) fcode-revision 20001 >=  
                       if instance then  
     ;  
  \ Create qec device node.  
     create-qec-properties  
     fload qec.fth                \ qec driver.  
  
  \ Create qe0 device node.  
     new-device  
        0 create-qe-properties  
        " network" device-type  
        fload qeinstance.fth      \ qe instance variables.  
        : dma-sync  ( virt-addr dev-addr size -- )  " dma-sync" $call-parent  ;  
        fload qe.fth              \ qe driver.  
        fload qe-package.fth      \ qe external methods.  
     finish-device  
  
  \ Create qe1 device node.  
     new-device  
        1 create-qe-properties  
        " network" device-type  
        fload qeinstance.fth      \ qe instance variables.  
        fload qe-package.fth      \ qe external methods.  
     finish-device  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  
  \ Create qe2 device node.  
     new-device  
        2 create-qe-properties  
        " network" device-type  
        fload qeinstance.fth      \ qe instance variables.  
        fload qe-package.fth      \ qe external methods.  
     finish-device  
  
  
  \ Create qe3 device node.  
     new-device  
        3 create-qe-properties  
        " network" device-type  
        fload qeinstance.fth      \ qe instance variables.  
        fload qe-package.fth      \ qe external methods.  
     finish-device  
  end0  
  
  \ -----------------------------------------------------------------  
  \ qec.fth  
  
  /locmem #channels / value chmem  
  chmem 2/ value rxbufsize  
  
  fload qed-util.fth      \ Not included, refer to example 2.  
  fload global.h.fth      \ Not included.  
  fload qecmap.fth        \ Not included, refer to example 2.  
  fload qectest.fth       \ Not included, refer to example 2.  
  
  : reset-qec-global  ( -- fail? )  
     gcr-reset qecg-control!      \ Issue global reset.  
     d# 100 set-ms-timeout  
     begin  
        qecg-control@ gcr-reset and  
     while  
        ms-timeout?  if ." Global reset failed" cr true exit then  
     repeat  
     false  
  ;  
  : qec-init  ( -- )  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     chmem qecg-memsize!  
     rxbufsize qecg-rxsize!  
     chmem rxbufsize - qecg-txsize!  
     gcr-burst16 qecg-control!    \ SBus parity disabled, Rx/Tx equal priority.  
  ;  
  : identify-chip  ( -- okay? )  qecg-control@ gcr-mode and gcr-mace =  ;  
  
  external  
  : open  ( -- true )  
     map-qec-regs  
     identify-chip dup  if  
        qec-init  
     else unmap-qec-regs  
     then  
  ;  
  : close ( -- )  qecg  if unmap-qec-regs then  ;  
  
  : selftest ( -- fail? )  
     qecg                 ( qecg )  
     map-qec-regs  
     qec-reg-test         ( qecg success? )  
     unmap-qec-regs  
     swap to qecg         ( success? )  
     0=                   ( fail? )  
  ;  
  
  : reset  ( -- )  
     qecg  
     map-qec-regs  
     reset-qec-global drop  
     unmap-qec-regs  
     to qecg  
  ;  
  
  headers  
  
  \ -----------------------------------------------------------------  
  \ qeinstance.fth  
  \ Define instance words for qe driver.  
  
  \ headerless  
  \ mace.h.fth:  
  0 instance value mace           \ virtual address of Mace registers base  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  \ channel.h.fth:  
  0 instance value qecc           \ virtual address of Qec channel registers base  
  \ qemap.fth:  
  instance variable my-channel#   \ qe channel#  
          my-channel# off  
  \ qecore.fth:  
  \ CPU base address of tmd, rmd, tbuf, rbuf rings.  
  0 instance value cpu-dma-base   \ base address of dma memory object viewed by cpu  
  0 instance value tmd0           \ transmit message descriptor#0  
  0 instance value rmd0           \ receive message descriptor#0  
  0 instance value tbuf0          \ base address of transmit buffer  
  0 instance value rbuf0          \ base address of receive buffers  
  \ IO (or dvice) base address of tmd, rmd, tbuf, rbuf rings.  
  0 instance value io-dma-base    \ base addr of dma memory object viewed by device  
  0 instance value io-tmd0        \ transmit message descriptor#0  
  0 instance value io-rmd0        \ receive message descriptor#0  
  0 instance value io-tbuf0       \ base address of transmit buffer  
  0 instance value io-rbuf0       \ base address of receive buffers  
  \ Required total Dma buffer size for all rings.  
  0 instance value qe-dma-size    \ Amount of memory mapped  
  \ *** Define required variables ***  
  instance variable status        \ Accumulated channel status word.  
  instance variable restart?      \ Restart? flag on after serious error.  
  instance variable nextrmd       \ Point to next rmd.  
  instance variable nexttmd       \ tmd0 nexttmd !, never changes presently  
  instance variable mode          \ To store loopback control & promiscuous info.  
  6 instance buffer: this-en-addr \ Contain ethernet address  
  instance defer .receive-error  
  instance defer .error  
  instance defer .transmit-error  
  \ timed-receive.fth:  
  instance variable alarmtime  
  instance defer handle-broadcast-packet  
  \ qetest.fth:  
  instance variable qe-verbose?   \ Flag for displaying diagnostic message.  
          qe-verbose? off  
  instance variable ext-lbt?      \ Flag for execution of external loopback test.  
          ext-lbt? off  
  \ qe0-package.fth:  
  6 instance buffer: macbuf       \ Contain mac address.  
  0 instance value obp-tftp       \ Contain ihandle of TFTP package.  
  instance variable qe-nbytes     \ Buffer size of higher layer receiver.  
  instance variable qe-buf        \ Buffer address of higher layer receiver.  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  
  headers  
  \ -----------------------------------------------------------------  
  \ qe.fth  
  
  : wlk-test ( mask addr #bits -- success? )  " wlk-test" $call-parent  ;  
  : set-ms-timeout  ( #ms -- )  " set-ms-timeout" $call-parent  ;  
  : ms-timeout?  ( -- flag )  " ms-timeout?" $call-parent  ;  
  
  fload mace.h.fth        \ Not included.  
  fload channel.h.fth     \ Not included.  
  fload qemap.fth         \ Not included, refer to example 2.  
  fload qecore.fth  
  fload timed-receive.fth  
  fload qeregtest.fth     \ Not included, refer to example 2.  
  fload qetest.fth  
  fload qe0-package.fth  
  
  \ -----------------------------------------------------------------  
  \ qe0-package.fth  
  \ Define the required methods for the network qe driver  
  
  set-my-channel#  
  external  
  : read  ( buf len -- -2 | actual-len )  
     qe0-read  
  ;  
  : write  ( buf len -- actual-len )  
     qe0-write  
  ;  
  : selftest  ( -- flag ) \ Flag 0 if passes test.  
     qe0-selftest  
  ;  
  : watch-net  ( -- )  
     qe0-watch-net  
  ;  
  : load  ( addr -- len )  
     qe0-load  
  ;  
  : open  ( -- okay? )  
     qe0-open  
  ;  
  : close  ( -- )  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     qe0-close  
  ;  
  : reset  ( -- )  
     qe0-reset  
  ;  
  
  headers  
  
  \ -----------------------------------------------------------------  
  \ qecore.fth  
  \ Main core of QEC/MACE per channel Tx/Rx drivers.  
  
  \  
  \ SQEC has the following features:  
  \       - Supports four independent IEEE 802.3 10BASE-T twisted pair interfaces.  
  \       - Supports SBus parity checking.  
  \       - Supports 32 bit of DVMA addressing.  
  \       - Automatic rejection/discard of receive/transmit packets  
  \         when receive/transmit suffers from errors.  
  \  
  
  headerless  
  \ *** Rx/Tx Ring Descriptor Layout ***  
  
  struct ( Rx/Tx Descriptor )  
  4 field >flags                  \ OWN, SOP, EOP, size/length  
  4 field >addr                   \ buffer address  
  ( total-length )  constant /md  
  
  hex  
  \ Definition for >flag field.  
  \ Bit[10:0] - Rx for W is buffer size, Rx for R is byte count, Tx for W is byte count.  
  8000.0000       constant own    \ For both Rx & Tx.  
  4000.0000       constant stp    \ For Tx only.  
  2000.0000       constant enp    \ For Tx only.  
       07ff       constant lenmask  
  
  \ Value to write to message descriptor to enable it for use  
  enp stp or own or       constant ready  
  
  \ *** buffer sizes and counts ***  
  
  \ Xmit/receive buffer structure.  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  \ This structure is organized to meet the following requirements:  
  \       - starts on an QEBURSTSIZE (64) boundary.  
  \       - qebuf is an even multiple of QEBURSTSIZE.  
  \       - qebuf is large enough to contain max frame (1518) plus  
  \               QEBURSTSIZE for alignment adjustments.  
  \  
  \ Similar to the 7990 ethernet controller, the QEC and the Software driver  
  \ communicate via ring descriptors. There are separate Rx & Tx descriptor  
  \ rings of 256 entries. Unlike 7990 the number of descriptor entries  
  \ is not programmable (fixed at 256 entries).  
  
  decimal  
   /md constant /rmd      \ rmd size = 8  
   /md constant /tmd      \ tmd size = 8  
  1792 constant /rbuf     \ 7*256 receive buffer size at least 1518+128=1636  
  1600 constant /tbuf     \ transmit buffer size  
   256 constant #rmds  
   256 constant #tmds  
  \  1 constant #tbufs    \ Just allocate one buffer for transmiter buffer pool.  
    32 constant #rbufs    \ # buffers allocated for receiver buffer pool.  
  
  #rmds /rmd *       value /rmds  
  #tmds /tmd *       value /tmds  
  
  headers  
  
  : restart?-on  ( -- ) restart? on  ;  
  
  \ Conversion between cpu dma address and io dma address.  
  : cpu>io-addr  ( cpu-addr -- io-addr )  cpu-dma-base - io-dma-base +  ;  
  : io>cpu-addr  ( io-addr -- cpu-addr )  io-dma-base - cpu-dma-base +  ;  
  
  \ buffer# to address calculations  
  : rmd#>rmdaddr  ( n -- addr )  /rmd * rmd0 +  ;  
  : rbuf#>rbufaddr  ( n -- addr )  #rbufs mod /rbuf * io-rbuf0 +  ;  
  : tmd#>tmdaddr  ( n -- addr )  /tmd * tmd0 +  ;  
  \ address to buffer# calculations  
  : rmdaddr>rmd#  ( addr -- n )  rmd0 - /rmd /  ;  
  
  \ *** Qe message descriptor ring access ***  
  
  \ Get current rx/tx message descriptor ring pointer (on CPU side).  
  : nextrmd@  ( -- cpu-rmd-addr )  nextrmd @  ;  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  : nexttmd@  ( -- cpu-tmd-addr )  nexttmd @  ;  
  
  \ get location of buffer  
  : addr@  ( rmd/tmd-addr -- buff-addr )  >addr rl@  ;  
  
  : status@  ( rmd/tmd-addr -- statusflag ) >flags rl@  ;  
  
  \ gets length of incoming message, receive only  
  : length@  ( rmdaddr -- messagelength )  >flags rl@ lenmask and  ;  
  
  \ Set current rx/tx message descriptor ring pointer (on CPU side).  
  : nextrmd!  ( cpu-rmd-addr -- )  nextrmd !  ;  
  : nexttmd!  ( cpu-tmd-addr -- )  nexttmd !  ;  
  
  \ Store buffer address into message descriptor  
  : addr!  ( buff-addr  rmd/tmd-addr -- )  >addr rl!  ;  
  
  \ Set length of message to be sent - transmit only  
  : length!  ( length rmd/tmd-addr -- )  >flags rl!  ;  
  
  \ *** Qe synchronization ***  
  
  \ Sync the message descriptor after cpu or device writes it.  
  : qesynciopb ( md -- )  
     dup cpu>io-addr /md           ( cpu-addr io-addr size )  
     dma-sync  
  ;  
  \ Sync the transmitting/received buffer after cpu/device writes it.  
  : qesyncbuf  ( md -- )  
     dup addr@ dup io>cpu-addr swap       ( md cpu-buf-addr io-buf-addr )  
     rot length@                          ( cpu-buf-addr io-buf-addr size )  
     dma-sync  
  ;  
  
  \ The buffer was already put back, put the descriptor in the chip's ready list  
  : give-buffer  ( rmd/tmd-addr -- )  
     dup >flags dup rl@ ready or swap rl!         ( md )  
    \ Sync the descriptor so the device sees it.  
     qesynciopb                                   ( )  
  ;  
  
  \ *** Qe error handling ***  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  : get-qe-status  ( -- channel-status )  
     qecc-status@ status @ or dup status !  
  ;  
  
  \ get receive errors, receive only  
  : rerrors@  ( -- errorsflag )  get-qe-status c-rerr-mask and ;  
  
  \ gets transmit errors, transmit only  
  : xerrors@  ( -- errorsflag ) get-qe-status c-terr-mask and ;  
  
  \ Clear transmit/receive/all error flags  
  : clear-terrors  ( -- ) status @ c-terr-mask not and status !  ;  
  : clear-rerrors  ( -- )  status @ c-rerr-mask not and status !  ;  
  : clear-errors  ( -- )  status off restart? off  ;  
  : clear-tint  ( -- ) status @ c-tint not and status !  ;  
  
  \ *** Basic initialization routines ***  
  
  \ words to set loopback control mode in UTR(R29) & promiscuous mode in MACCC(R13)  
  \ Bit<7> to control promiscuous mode, Bits<2:1> to control loopback mode,  
  \ Bit<0> to test the cable connection.  
  1 constant m-cable  
  
  : set-loop-mode  ( -- )  mode @ m-loop-mask and m-rpa or mace-utr!  ;  
  : set-prom-mode  ( -- ) mode @ m-prom and mace-maccc!  ;  
  : check-cable-mode?  ( -- flag )  mode @ m-cable =  ;  
  : external-loopback?  ( -- flag )  mode @ m-loop-mask and m-loop-ext =  ;  
  
  \ Check existence of no-tpe-test property to initialize disable-tpe-link-test bit.  
  \ Enable tpe-link-test if the property doesn't exist,  
  \       or disable tpe-link-test if the property exists.  
  : init-link-test  ( -- )  
    \ Disable link test for external loopback mode.  
     external-loopback?  if  m-dlnktst mace-phycc! exit  then  
     " no-tpe-test" get-my-property  if  0  
     else  2drop m-dlnktst  then  
     mace-phycc!  
  ;  
  \ Enable/disable tpe-link-test  
  : setup-link-test  ( enable-flag -- )  
     " no-tpe-test" " get-property" eval  if  
                  \ Property doesn't exist, already enabled.  
        0=  if  0 0 " no-tpe-test" property  then  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     else  2drop                          \ Currently disabled.  
        if  " no-tpe-test" delete-property  then  
     then  
  ;  
  \  
  \ After doing a port select of the twisted pair port, the  
  \ driver needs to give ample time for the MACE to start  
  \ sending pulses to the hub to mark the link state up.  
  \ Loop here and check of the link state has gone into a  
  \ pass state.  
  \  
  : link-state-fail?  ( -- fail? )  
     d# 1000 set-ms-timeout  
     begin  
        mace-phycc@ m-lnkst and  
     while  
        ms-timeout?  if  
           check-cable-mode?  if  
              ."  failed, transceiver cable problem? or check the hub." cr  
              true  
           else  
  \           m-dlnktst mace-phycc!  
              false  
           then  
           exit  
        then  
     repeat  
     check-cable-mode?  if  ." passed."  cr  then  
     false  
  ;  
  
  : set-physical-address  ( -- )  
     m-addrchg mace-iac!  
     begin  mace-iac@ m-addrchg and 0=  until  
     m-phyaddr mace-iac!  
    \ Store least significant byte first.  
     this-en-addr 6 bounds  do  i c@ mace-paddr!  loop  
     0 mace-iac!  
  ;  
  
  : set-address  ( en-addr len -- )  
    drop  this-en-addr 6 move  ;  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  : set-logaddr-filter  ( -- )  
     m-addrchg mace-iac!  
     begin  mace-iac@ m-addrchg and 0=  until  
     m-logaddr mace-iac!  
     8 0  do  0 mace-laddrf!  loop  
     0 mace-iac!  
  ;  
  
  \ Reset (or stop) the qec channel.  
  \       Issue a soft reset to the desired Mace.  
  \       Then issue a soft reset to the desired channel in QEC.  
  \ Chip reset algorithm:  
  \       Set the reset bit then wait until the reset bit cleared.  
  \ Timeout in 0.1 sec if fail.  
  \  
  
  : channel-reset  ( -- fail? )  
     m-swrst mace-biucc!          \ Issue Mace reset.  
     d# 100 set-ms-timeout  
     begin  
        mace-biucc@ m-swrst and  
     while  
        ms-timeout? if ." Cannot reset Mace" cr true exit then  
     repeat  
     c-rst qecc-control!          \ Reset QEC channel registers.  
     d# 100 set-ms-timeout  
     begin  
        qecc-control@ c-rst and  
     while  
        ms-timeout?  if  ." Cannot reset QEC channel" cr true exit  then  
     repeat  
     false  
  ;  
  
  \ Initialize a single message descriptor  
  : rmd-init  ( rbufaddr rmdaddr -- )  
     /rbuf over length!             \ Buffer length  
     addr!                          \ Buffer address  
  ;  
  
  \ Set up the data structures necessary to receive a packet  
  : init-rxring  ( -- )  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     rmd0 nextrmd!  
     #rmds 0  do   i rbuf#>rbufaddr  i rmd#>rmdaddr  rmd-init  loop  
  ;  
  \  
  \ Initially first N=#rbufs descriptors with one-to-one association with a  
  \ buffer are made ready, the rest (256-N) not ready, then turn on receiver.  
  \ Whenver a receive buffer is processed, the information is copied out,  
  \ the buffer will be linked to the ((current+N)%256) entry then make the  
  \ entry is ready. Ie. The window of N ready descriptor/buffer pair is  
  \ moving around the ring.  
  \  
  : enable-rxring  ( -- )  
     #rbufs 0  do  i rmd#>rmdaddr give-buffer  loop  
  ;  
  
  \ transmit buffer initialize routine  
  : init-txring ( -- )  
     tmd0 nexttmd!  
     #tmds 0  do io-tbuf0 i tmd#>tmdaddr addr!  loop  
  ;  
  
  \ *** Receive packet routines ***  
  
  \ Utility words used in .rerr-text & .terr-text.  
  : bits  ( mask #right-bits -- mask' right-bits )  
     >r dup /n 8 * r@ - tuck << swap >>    ( mask bits ; RS: #bits )  
     swap r> >> swap                      ( mask' bits )  
  ;  
  
  : 1bit  ( mask -- mask' rightest-bit-value )  1 bits  ;  
  : .rerr-text  ( -- )  
     rerrors@  
     1bit  if  ." SBus Rx Error Ack  " restart?-on  then  
     1bit  if  ." SBus Rx Parity  " restart?-on  then  
     1bit  if  ." SBus Rx Late  " restart?-on  then  
     1bit  if  ." Data Buffer Too Small  "  then  
  \  1bit  if  ." Rx packet Dropped  "  then  
     1bit drop                            \ Skip drop error, happens all the time  
     1bit drop                            \ Skip receive interrupt bit.  
     1bit  if  ." CRC error  "  then  
     1bit  if  ." Framing error  "  then  
     1bit  if  ." MACE Rx Late Collision  "  then  
     1bit  if  ." MACE FIFO overflow "  then  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     1bit  if  ." MACE Missed Counter Overflow  "  then  
     1bit  if  ." MACE Runt Counter Overflow  "  then  
     1bit  if  ." MACE Rx Coll Counter Overflow  "  then  
     1bit  if  ." Collision error " then  
     drop cr  
  ;  
  
  : (.receive-error  ( -- )  
     rerrors@  if  .rerr-text  then  
  ;  
  ' (.receive-error to .receive-error  
  ' (.receive-error to .error  
  
  : to-next-rmd  ( -- )  
     /rmd nextrmd +!  
     nextrmd@ rmd0 - /rmds >=  if  rmd0 nextrmd!  then  
  ;  
  
  \  *** Transmit packet routines ***  
  
  : to-next-tmd  ( -- )  
     /tmd nexttmd +!  
     nexttmd@ tmd0 - /tmds >=  if  tmd0 nexttmd!  then  
  ;  
  
  \ Ignores the size argument, and uses the standard buffer.  
  : get-buffer  ( dummysize -- buffer )  
     drop  nexttmd@ addr@         ( io-tbuf )  
     io>cpu-addr                  ( cpu-tbuf )  
  ;  
  
  \ Display time domain reflectometry information  
  \ : .tdr  ( -- )  ;  
  
  : .terr-text  ( -- )  
     xerrors@  
     d# 16 bits drop                      \ Skip the receiver bits.  
     1bit  if  ." SBus Tx Error Ack  " restart?-on  then  
     1bit  if  ." SBus Tx Parity  " restart?-on  then  
     1bit  if  ." SBus Tx Late  " restart?-on  then  
     1bit  if  ." QEC Chained Tx Descriptor Error  " restart?-on  then  
     1bit  if  ." QEC Tx Retry Counter Overflow  "  then  
     1bit drop                            \ Skip transmit interrupt bit  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     1bit  if  ." MACE >1518 Babble  "  then  
     1bit  if  ." MACE Jabber  "  then  
     1bit  if  ." MACE FIFO Underflow  "  then  
     1bit  if  ." Tx Late Collision  "  then  
     1bit  if  ." Too Many Retries  "  then  
     1bit  if  ." Lost Carrier  (transceiver cable problem?)  "  then  
     1bit  if  ." Excessive Defer  "  then  
     drop cr  
  ;  
  
  \ print summary of any HARD errors  
  : (.transmit-error  ( -- )  
     xerrors@  if  .terr-text  then  
  ;  
  ' (.transmit-error to .transmit-error  
  
  \ Set up CPU page maps  
  : map-qe-buffers  ( -- )  
     #rbufs /rbuf *  
  \ 2KB (8*256) for tmds & 2KB (8*256) for rmds & 4KB for tbuf  
  \ ie. one page for tmds & rmds, one page for tbuf, the rest for rbufs.  
     h# 2000 +  
     to qe-dma-size  
  
     \ Allocate and map that space  
     qe-dma-size dma-alloc                ( dma-addr )  
  
     \ Set the addresses of the various DMA regions used by the cpu.  
     dup  to cpu-dma-base  
     dup  to tmd0    h# 800 +  ( next-address )  
     dup  to rmd0    h# 800 +  ( next-address ) \ Enough for 256 entries  
     dup  to tbuf0   h# 1000 + ( next-address ) \ Enough for max packet  
          to rbuf0             ( )  
     tmd0 qe-dma-size false dma-map-in    ( io-dma-addr )  
     \ Set the addresses of the various DMA regions used by the qec chip.  
     dup  to io-dma-base  
     dup  to io-tmd0    h# 800 +  ( next-address )  
     dup  to io-rmd0    h# 800 +  ( next-address ) \ Enough for 256 entries  
     dup  to io-tbuf0   h# 1000 + ( next-address ) \ Enough for max packet  
          to io-rbuf0             ( )  
  
  ;  
  : unmap-qe-buffers  ( -- )  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     tmd0 io-tmd0 qe-dma-size dma-map-out  
     tmd0 qe-dma-size dma-free  
     0 to tmd0  
  ;  
  
  \ *** Chips initialization routines ***  
  
  \ Initializes the QEC/Mace chips.  
  : channel-init ( -- fail? )  
    \ *** Initialize QEC per channel registers.  
     io-rmd0 qecc-rxring!  
     io-tmd0 qecc-txring!  
     c-rintmask qecc-rintmask!            \ Mask RINT.  
     c-tintmask qecc-tintmask!            \ Mask XINT.  
     my-chan# chmem * dup qecc-lmrxwrite! dup qecc-lmrxread!  
     rxbufsize + dup qecc-lmtxwrite! qecc-lmtxread!  
     c-qecerrmask qecc-qecerrmask!  
     c-macerrmask qecc-macerrmask!  
    \ *** Initialize MACE registers.  
  \  0 mace-xmtfc!  
     m-apadxmt mace-xmtfc!        \ Set auto pad transmit for transmit frame control  
     0 mace-rcvfc!                \ Init. receive frame control.  
    \ Init. Interrupt Mask Register to mask rcvint & cerr and unmask xmtint  
    \     according QEC spec.  
     m-cerrm m-rcvintm or mace-imr!  
    \ Init. Bus Interface Unit Configuration Control to transmit after 64 bytes  
    \     have been loaded & byte swap.  
     m-xmtsp64 m-xmtspshift << m-bswp or mace-biucc!  
    \ Init. FIFO Conf Control to set transmit/receive fifo watermark update  
     m-xmtfw16 m-rcvfw32 or m-xmtfwu or m-rcvfwu or mace-fifocc!  
     m-10base-t mace-plscc!       \ Select twisted pair mode.  
     init-link-test               \ Init. tpe link test mode.  
     set-physical-address         \ Set mac address.  
     set-logaddr-filter           \ Set logical address filter.  
     0 mace-iac!  
     link-state-fail?             \ Wait and check the link state marked up.  
     mace-mpc@ drop       \ Read to reset counter and to prevent an invalid int.  
     set-loop-mode        \ Set UTR  
     set-prom-mode        \ Set MACCC  
     m-apadxmt not mace-xmtfc@ and mace-xmtfc!  
     m-astrprcv not mace-rcvfc@ and mace-rcvfc!  
  ;  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  \ Turn on the Mace, ready to tx/rx packets.  
  : enable-mace  ( -- )  
     m-enxmt m-enrcv or mace-maccc@ or mace-maccc!  
  ;  
  
  \ *** Ethernet on/off routines ***  
  
  \ Initializes the QEC/Mace chips, allocating the necessary memory,  
  \ and enabling the transmitter and receiver.  
  : net-on  ( -- flag )           \ true if net-on succeeds  
     clear-errors  
     mac-address set-address  
     channel-reset 0=  if  
        init-txring  
        init-rxring  
        channel-init 0= dup  if  
           enable-rxring  
           enable-mace  
        then  
     else  false  
     then  
  ;  
  
  \ Stop the activity of this net channel.  
  : net-off  ( -- )  channel-reset drop init-link-test  ;  
  
  \ *** Main receive routines ***  
  
  \  
  \ Whenver a receive buffer is processed, the information is copied out,  
  \ the buffer will be linked to the ((current+N)%256)th entry then make the  
  \ entry is ready ie.the window of N ready descriptor/buffer pair is  
  \ moving around the ring.  
  \  
  \ If 256 (#rmds) is multiples of N (#rbufs=32), we don't need to link the  
  \ next-ready-rmd with the current processed rx buffer dynamically. They can  
  \ be set at the initialization time statically. For run time, we just need  
  \ to make the ((current+N)%256)th rmd ready.  
  \  
  : return-buffer ( buf-handle -- )  
     rmdaddr>rmd#                         ( [io-rbuf] rmd# )  
     #rbufs + #rmds mod                   ( [io-rbuf] next-ready-rmd# )  
     rmd#>rmdaddr                         ( [io-rbuf] next-ready-rmd )  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     dup addr@ over rmd-init              ( next-ready-rmd ; Set length )  
     give-buffer                          ( ; Make it ready )  
     to-next-rmd                          \ Bump SW nextrmd to next one  
  ;  
  : receive-ready? ( -- packet-waiting? )  
     restart? @  if  net-on drop  then  
     nextrmd@                                     ( rmd )  
    \ Sync RMD before CPU looking at it.  
     dup qesynciopb                               ( rmd )  
     status@ own and 0=                           ( flag )  
  ;  
  : receive  ( -- buf-handle buffer len )         \ len non-zero if packet ok  
     nextrmd@ dup addr@                   ( rmd io-rbuf-addr )  
     io>cpu-addr                          ( rmd cpu-rbuf-addr )  
     over length@                         ( rmd cpu-rbuf-addr len )  
     rerrors@  if  
        .receive-error clear-rerrors  
     then  
     dup  if                              ( rmd cpu-rbuf-addr len )  
    \ Sync the received buffer before CPU looking at it.  
        nextrmd@ qesyncbuf                ( rmd cpu-rbuf-addr len )  
     then  
  ;  
  \ *** Main transmit routines ***  
  : set-timeout  ( interval -- )  get-msecs  +  alarmtime !  ;  
  : timeout?  ( -- flag )  get-msecs  alarmtime @ >=  ;  
  : 10us-wait  ( -- )  d# 10 begin 1- dup 0= until drop  ;  
  
  
  
  \ Wait until transmission completed  
  : send-wait  ( -- )  
  \ Wait the packet to get to the local memory, ready for MACE to xmit.  
     d# 2000 set-timeout          \ 2 second timeout.  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     begin  
        get-qe-status  
        c-tint and                \ Transmit interrupt bit set?  
        timeout? or               \ Or timeout?  
     until  
     timeout?  if  
        ." TINT was not set!" cr true exit  
     then  
    \ Transmit completion, sync TMD before looking at it.  
     nexttmd@ dup qesynciopb      ( tmd )  
     status@ own and  if          ( flag )  
        ." Tx descriptor still owned by QEC!" cr  
     then  
  \ Wait the packet to get to net, make sure at most one xmit packet in MACE FIFO.  
     d# 1000 set-timeout          \ 1 second timeout.  
     begin  
        10us-wait  
        qecc-lmtxwrite@ qecc-lmtxread@ =  
        timeout? or  
     until  
     timeout?  if  
        ." Tx packet not out to net!" cr  
     then  
     false  
  ;  
  
  \ This send routine does not enforce the minimum packet length.  It is  
  \ used by the loopback test routines.  
  :  short-send  ( buffer length -- error? )  
     clear-tint                   \ Erase tint status bit.  
    \ discard buffer address, assumes using nexttmd  
     nip nexttmd@                 ( length tmd )  
     tuck length!                 ( tmd ; Set length )  
    \ Sync the transmit buffer so the device sees it.  
     dup qesyncbuf                ( tmd )  
     give-buffer                  ( ; Give tmd to chip )  
     c-tdmd qecc-control!         \ Bang the chip, let chip look at it right away  
     send-wait                    ( fail? )       \ wait for completion  
     xerrors@ dup  if             ( fail? error? )  
        .transmit-error clear-terrors  
     then  or                     ( error? )  
     to-next-tmd                  ( error? )  
     restart? @  if  net-on drop  then    ( error? )  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     c-hard-terr-mask and         ( hard-error? )  
  ;  
  
  \ Transmit packet routine, no S/W retry on this layer.  
  : net-send  ( buffer length -- error? )  \ error? is contents of chan-status  
     d# 64 max                    \ force minimum length to be 64  
     short-send                   ( error? )  
  ;  
  
  \ -----------------------------------------------------------------  
  \ timed-receive.fth  
  \ Implements a network receive that will timeout after a certain interval.  
  
  decimal  
  
  : multicast? ( handle data-address length -- handle data-address length flag )  
     \ Check for multicast/broadcast packets  
     over                        ( ... data-address )  
     c@ h# 80 and dup  if        \ Look at the multicast bit  
         ( handle data-address length multicast? )  
         handle-broadcast-packet  
     then  
  ;  
  
  : receive-good-packet  ( -- [ buffer-handle data-address length ]  | 0 )  
     begin  
        begin  
           timeout?  if  false exit  then  
           receive-ready?  
        until  
        receive dup 0=  
     while  
        .error  2drop return-buffer  
     repeat  
  ;  
  : receive-unicast-packet  ( -- [ buffer-handle data-address length ] | 0 )  
     begin  
        receive-good-packet  dup 0=  if  exit  then  
        multicast?  
     while  
        2drop return-buffer  
     repeat  
  ;  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  \ Receive a packet, filtering out broadcast packets and timing  
  \ out if no packet comes in within a certain time.  
  : timed-receive ( timeout-msecs -- [ buffer-handle data-address length ] err?)  
     set-timeout  receive-unicast-packet ?dup 0=  
  ;  
  
  \ -----------------------------------------------------------------  
  \ qetest.fth  
  \ Define Qec/Mace loopback-test, net-init & watch-test routines.  
  
  \ This file contains Qec/Mace selftest routines.  
  \ It defines the following external words:  
  \       loopback-test  ( internal/external-flag -- success? )  
  \       net-init  ( -- success? )  
  \       watch-test ( -- )  
  \ Also it defines the following external variable.  
  \       qe-verbose?     - Flag to indicate if want the test messages displayed.  
  \       ext-lbt?        - Flag to indicate if run the external loopback test.  
  \  
  \ The algorithme for the loopback test:  
  \       Set internal or external loopback with no promiscuous mode.  
  \       Turn on the Qec/Mace Ethernet port.  
  \       If it succeeds, send out a short packet containing walking 0/1 patterns.  
  \       If it succeeds, wait for a period, check if receive the loopback packet.  
  \       If so, verify the length of the received packet is right.  
  \       Also check if the data of the received packet is right.  
  \       Return true if everything is fine, otherwise return false.  
  
  hex  
  headerless  
  create loopback-prototype  
     ff c, 00 c,                                        \ Ones and zeroes  
     01 c, 02 c, 04 c, 08 c, 10 c, 20 c, 40 c, 80 c,    \ Walking ones  
     fe c, fd c, fb c, f7 c, ef c, 0df c, 0bf c, 7f c,  \ Walking zeroes  
     55 c, aa c,  
  
  : loopback-buffer  ( -- addr len )  
     d# 32 get-buffer  ( addr )  
     mac-address drop    over              6 move   \ Set source address  
     mac-address drop    over 6 +          6 move   \ Set destination address  
     loopback-prototype  over d# 12 +  d# 20 move   \ Set buffer contents  
     d# 32  
  ;  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  
  : pdump  ( addr -- )  
     base @ >r  hex  
     dup      d# 10  bounds  do  i c@  3 u.r  loop  cr  
     d# 10 +  d# 10  bounds  do  i c@  3 u.r  loop  cr  
     r> base !  
  ;  
  
  \ Print loopback control type for verbose mode.  
  : .loopback  ( -- )  
     mode @  m-loop-mask and  
     ?dup  if  
        dup m-loop-ext =  if  ." External " drop  
        else  ." Internal " m-loop-intmen =  if  ." (including Mendec) "  then  
        then  
        ." loopback test -- "  
     then  
  ;  
  
  \ Print loopback control type for non-verbose mode,  
  \ it is used after any error occurs.  
  : ?.loopback  ( -- )  
     qe-verbose? @  0=  if  .loopback  then  
  ;  
  
  : switch-off  ( -- false )  qe-verbose? off  false  ;  
  
  : bad-rx-data  ( buf-handle data-address -- false )  
     ?.loopback  
     ." Received packet contained incorrect data.  Expected: " cr  
     loopback-prototype pdump  
     ." Observed:" cr  
     d# 12 + pdump  
     switch-off  
  ;  
  \ Check the data of the received packet, return true if data is ok.  
  : check-data  ( buf-handle data-address length -- ok? )  
     drop  ( buf-handle data-address )  
     dup d# 12 +  loopback-prototype  d# 20 comp  
     if  bad-rx-data  
     else  drop   ( buf-handle )  
       return-buffer  
       qe-verbose? @  if  ." succeeded." cr  then  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
       mode off   true  
     then  
  ;  
  
  \ Check the length & data of the received packet, return true if data & len ok.  
  : check-len&data  ( buf-handle data-address length -- ok? )  
     \ The CRC is appended to the packet, thus it is 4 bytes longer than  
     \ the packet we sent.  
     dup d# 36 <>  
     if  ?.loopback  
       ." Wrong packet length; expected 36, observed " .d cr  
       switch-off  
     else  check-data  
     then  
  ;  
  
  headers  
  \ Run internal or external loopback test, return true if the test passes.  
  : loopback-test  ( internal/external -- pass? )  
     mode !  
     qe-verbose? @  if  ."  " .loopback  then  
     net-on  if  
        loopback-buffer short-send  if  
           ?.loopback  ." send failed." cr  
           switch-off  
        else  
           d# 2000 timed-receive  if  
              ?.loopback  
              ." Did not receive expected loopback packet." cr  
              switch-off  
           else         (  buf-handle data-address length )  
              check-len&data  
           then  
        then  
     else  
        switch-off  
     then  
     net-off  mode off  
  ;  
  
  \ If there is a normal external loopback test, then we don't need this.  
  \ MACE external loopback test requires a special cable. Don't run external  
  \ loopback test for selftest & watch-net.  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  : check-cable?  ( -- ok? )  
     m-cable mode !  ."  Link state check -- "  
     net-on               ( success? )  
     net-off mode off  
  ;  
  \ Turn on the Ethernet port after pass loopback test.  
  \ Return true if net-init succeeds, otherwise return false if it fails.  
  : net-init  ( -- flag )  
     mode @               \ Save requested mode because loopback changes it.  
     m-loop-int loopback-test  
     if                   ( mode-saved ; Pass internal loopback test. )  
        ext-lbt? @        \ Run external loopback test if the ext-lbt? flag is set.  
     \ qe internal loopback with mendec is equivalent to external loopback of le.  
        if  m-loop-intmen loopback-test  else  true  then ( mode-saved )  
        swap mode !       \ Restore the mode.  
        if  net-on        \ Pass loopback test, turn on the ethernet port.  
        else  false  
        then  
     else  mode ! false  
     then  
  ;  
  
  headerless  
  : wait-for-packet  ( -- )  
     begin  key?  receive-ready?  or  until  
  ;  
  
  headers  
  \ Check for incoming Ethernet packets.  
  \ Use promiscuous mode to check for all incoming packets.  
  : watch-test  ( -- )  
     ." Looking for Ethernet packets." cr  
     ." '.' is a good packet.  'X' is a bad packet."  cr  
     ." Type any key to stop."  cr  
     begin  
        wait-for-packet  
        receive-ready?  
        if  receive  
          if   ." ."  else  ." X"  then  
            drop  return-buffer  
        then  
        key? dup  if  key drop  then  
     until  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  ;  
  
  \ -----------------------------------------------------------------  
  \ qe0-package.fth  
  \ Implements the architectural interface for the qe driver  
  
  headerless  
  \  
  \ The network driver uses the standard "obp-tftp" support package for  
  \ implementation. The "obp-ftfp" package implements the Internet Trivial File  
  \ Transfer Protocol (TFTP) for use in network booting. The "obp-tftp" package  
  \ defines the following methods to be used by the network driver:  
  \       open    ( -- okay? )  
  \       close   ( -- )  
  \       load    ( addr -- size )  
  \ The "obp-tftp" package uses the read and write methods of the network driver  
  \ for receiving and transmitting packets. The package assums the size of the  
  \ maximum transfer packet is 1518 bytes. If the network driver needs bigger  
  \ maximum packet size, then it requires the method "max-transfer" defined,  
  \ the method will be called by the obp-tftp package to define the maximum  
  \ transfer packet size.  
  \  
  : init-obp-tftp  ( -- okay? )  
     " obp-tftp" find-package  if         ( phandle )  
        my-args rot open-package          ( ihandle )  
     else  0  
     then  
     dup to obp-tftp                      ( ihandle | 0 )  
     dup 0=  if  
        ." Can't open OBP standard TFTP package"  cr  
     then  
  ;  
  : set-my-channel#  ( -- )  
  \ If don't find the channel property, use 0.  
     " channel#" get-my-property  if  0  else  decode-int nip nip  then  
     my-channel#!  
  ;  
  
  headers  
  : qe-xmit  ( bufaddr nbytes -- #sent )  
     tuck get-buffer                      ( nbytes bufaddr ether-buffer )  
     tuck  3 pick  move                   ( nbytes ether-buffer )  
     over net-send  if  drop 0  then      ( #sent )  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  ;  
  
  : qe-poll  ( bufaddr nbytes -- #received )  
     qe-nbytes ! qe-buf !                 ( )  
     receive-ready?  0=  if  0 exit  then    \ Bail out if no packet ready  
     receive ?dup  if                     ( rmd ether-buffer length )  
        dup >r                            ( rmd ether-buffer length )  
        qe-nbytes @ min                   ( rmd ether-buffer length' )  
        qe-buf @ swap move                ( rmd )  
        return-buffer  r>                 ( #received )  
     else  
        drop return-buffer 0              ( 0 )  
     then  
  ;  
  : set-vectors  ( -- )  
     ['] (.receive-error  to .error  
     ['] (.transmit-error to .transmit-error  
     ['] noop to handle-broadcast-packet  
  ;  
  : map-qe  ( -- )  
     mace 0=  if          \ Do mapping if it is unmapped.  
        map-chips  
        map-qe-buffers  
     then  
  ;  
  : unmap-qe  ( -- )  
     mace  if             \ Do unmapping if it is mapped.  
        unmap-qe-buffers  
        unmap-chips  
     then  
  ;  
  : qe-loopback-test  ( -- flag )         \ flag true if passes test  
     set-vectors  
     mode off  qe-verbose? on  
     ext-lbt? on  
     net-init  
     ext-lbt? off  
     dup  if  net-off drop check-cable?  then  
     qe-verbose? off  
  ;  
  : (watch-net) ( -- )  
     map-qe  
     set-vectors  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
     m-prom mode !  
     qe-verbose? off  
     ext-lbt? off  
     net-init  if  watch-test net-off  then  
     unmap-qe  
  ;  
  
  external  
  : qe0-read  ( buf len -- -2 | actual-len )  
     qe-poll  ?dup  0=  if  -2  then  
  ;  
  : qe0-write  ( buf len -- actual-len )  qe-xmit  ;  
  : qe0-selftest  ( -- flag )     \ Flag 0 if passes test.  
     map-qe  
     qe-reg-test       ( success? )  
     if  
        qe-loopback-test 0=       \ Alternate the return flag.  
     else  
        true  
     then           ( failure? )  
     unmap-qe  
  ;  
  : qe0-watch-net  ( -- )  
     qe0-selftest 0=  if  (watch-net)  then  
  ;  
  
  : qe0-load  ( addr -- len ) " load" obp-tftp $call-method  ;  
  : qe0-open  ( -- okay? )  
     map-qe  
     set-vectors  
     mode off  qe-verbose? off  
     net-init 0=  if  unmap-qe false exit  then  
  
     mac-address drop macbuf 6 move       \ Update macbuf.  
     macbuf 6 encode-string  " mac-address" property  
  
     init-obp-tftp 0=  if  close false exit  then  
     true  
  ;  
  : qe0-close  ( -- )  
     obp-tftp ?dup  if  close-package  then  
     mace  if  net-off  then  
     unmap-qe  

Code Example 9 QED Bootable Driver Sample (Continued)

  \ QED bootable driver  
  ;  
  : qe0-reset  ( -- )  
     mace  if  net-off  
     else  map-chips net-off unmap-chips  then  
  ;  
  headers