"Writing FCode Programs for PCI" To assist PCI developers in creating Open Firmware compliant products, FirmWorks has created "Writing FCode Programs for PCI." This manual, available exclusively from FirmWorks, is an adaptation of previously-existing FCode programming documentation that has been customized for Open Firmware and PCI. The book clearly explains the theory and practice of writing and producing FCode programs and is filled with program examples to help you get started. If you are making the move to Open Firmware on PCI, this book will help you make a smooth transition. A complete table of contents for the manual is provided at the end of this file. In it, you will find chapters describing packages, properties, the FCode command set, each of the standard device types, and more. To order your copy, send this order form to: info@firmworks.com Ordering Information -------------------- We accept orders by mail, email, phone or fax. Payment can be made by check or credit card (Visa/MasterCard/Diners Club/Carte Blanche/JCB. Sorry. We do not accept American Express.) If paying by credit card, be sure to include the card's expiration date and the billing address of the card if it is different from the shipping address. Please also include your phone number and email address, in case we have a question about your order. Normally, shipment to US addresses is by Priority Mail and to all other destinations is by airmail. On request, we will also ship by overnight carrier for $5 handling plus our cost [specify carrier and rate (e.g. overnight/second day/economy) and include your phone number]. Order Form --------- Name: _______________________________________________________________ Company: _______________________________________________________________ Address 1: _______________________________________________________________ Address 2: _______________________________________________________________ City: _____________________________________ State: ____ Zip: ________ Country: _______________________________________________________________ Phone: __________________ (In case we have questions about your order) Email: _______________________________________________________________ _____ Copies @ $60.00 $ __________ Shipping and handling $ __________ (15% of book total for U.S. and Canada; 45% elsewhere; overnight for $5 plus actual cost) Subtotal $ __________ Sales tax (8.25% for California residents) $ __________ Total (in U.S. dollars) $ __________ Shipping method --------------- __ Standard __ Expedited (Specify carrier/rate __________________________) Payment method -------------- __ Visa __ MasterCard __ Diners Club __ Carte Blanche __ JCB __ Check (Sorry. We do not accept American Express.) Card Number: _________________________________________ Expires: __________ Signature: _______________________________________________________________ Terms ----- 1. Prices, offerings and terms subject to change without notice. 2. Prices F.O.B. FirmWorks office. Risk of loss passes to customer at time of delivery to carrier. The following are extracts from the book. ---------------------------------------------------------------------- CONTENTS Preface xi Chapter 1: PCI Cards and FCode 1 The Purpose of FCode 1 Locating the FCode Program 1 FCode Program Functions 2 FCode ROM Format 3 Interpreting FCode 3 Device Identification 3 Creating and Executing FCode Definitions 4 Chapter 2: Elements of FCode Programming 5 Colon Definitions 6 Stack Operations 6 Additional Information 7 Programming Style 7 Commenting Code 7 Coding Style 8 Short Definitions 8 Stack Comments 8 A Minimum FCode Program 10 FCode Classes 12 Primitive FCode Functions 13 System FCode Functions 14 Interface FCode Functions 14 Local FCode Functions 15 Chapter 3: Testing FCode Programs 17 FCode Source 17 Tokenizing FCode Source 18 FCode Binary Format 18 PCI Expansion ROM Header 19 FirmWorks pci-header / pci-header-end Tokenizer Extensions 19 Testing FCode Programs on the Target Machine 20 Configuring the Target Machine 21 Setting Appropriate Configuration Parameters 21 "The Script" and the Open Firmware Startup Sequence 22 Modifying the Expansion Bus Probe Sequence 23 Getting to the User Interface 24 Using the Command Line Editor of the User Interface 24 Using the User Interface to Test FCode Programs 26 Using dl to Load From a Serial Port 27 Downloading Multiple Files with dl and fload 28 Using the User Interface to Interpret an FCode Program 32 Using the User Interface to Browse a Device Node 34 Using the User Interface to Test a Device Driver 35 Device Node Methods 35 Testing FCode Programs in Source Form 39 Producing an FCode ROM 40 Exercising an Installed FCode ROM 40 Chapter 4: Packages 41 Packages and Instances 41 Package Data 45 Static and Instance-specific Methods 46 Defining Methods, Properties and Data 46 Execution Tokens 47 Intra-package Calling Methods 47 Accessing Other Packages 47 Inter-package Calling Methods 49 execute-device-method and apply 51 Plug-in Device Drivers 52 Common Package Methods 52 Basic Methods 53 Supplemental Methods 53 Package Data Definitions 54 Instance Arguments and Parameters 55 Package Addresses 56 Package Mappings 57 Modifying Package Properties 57 Standard Support Packages 57 TFTP Booting Support Package 58 Deblocker Support Package 59 Disk-Label Support Package 61 Chapter 5: Properties 63 Standard FCode Properties 65 Standard Property Names 65 Display Device Properties 65 Network Device Properties 66 Memory Device Properties 66 MMU Properties 66 General Properties For Parent Nodes 67 Properties For PCI Parent Nodes 67 Properties for PCI Child Nodes 68 Detailed Descriptions of Standard Properties 69 Manipulating Properties 84 Property Creation and Modification 84 Property Values 85 Property Encoding 85 Property Retrieval 85 Property Decoding 86 Property-Specific FCodes 87 Chapter 6: FCode Basic Concepts 89 Parent-Relative Addressing 89 PCI Configuration Space 90 PCI Configuration Space Header 91 Open Firmware Memory Types 93 System Memory 93 Scratch Buffer 94 DMA Memory 94 Device Memory 96 Chapter 7: Block and Byte Devices 99 Block Devices 99 Byte Devices 99 Required Methods 100 Required Properties 101 Device Driver Examples 102 Simple Block Device Driver 102 Extended Block Device Driver 103 Complete Block and Byte Device Driver 109 Chapter 8: Network Devices 121 Required Methods 121 Required Device Properties 122 Optional Device Properties 122 network Device Driver Issues 122 write Buffer Format 122 read Buffer Format 124 Use of DMA 124 selftest 124 Device Driver Examples 124 Simple Bootable Network Device Example 124 Chapter 9: Serial Devices 131 Required Methods 131 Required Properties 131 Device Driver Examples 132 Simple Serial FCode Program 132 Complete Serial FCode Program 132 Chapter 10: Display Devices 139 Required Methods 139 Required Properties 140 Structure of a display Device Driver 140 Probe Time Actions 140 is-install Actions 140 is-remove Actions 142 is-selftest Actions 142 display Device Driver Issues 142 16-Color Text Extension Recommended Practice 142 8-Bit Graphics Extension 144 Use of Legacy VGA Addressing 145 Device Driver Example 145 Generic VGA Display Device Driver 145 Chapter 11: Memory-Mapped Buses 153 Required Methods 153 PCI Bus Addressing 156 PCI Required Properties 156 SBus Addressing 157 SBus Required Properties 157 VMEBus Addressing 157 VMEBus Required Properties 158 Chapter 12: Open Firmware Dictionary 159 Appendix A: FCode Reference 341 FCode Primitives 341 FCodes by Function 342 FCodes by Byte Value 361 FCodes by Name 372 Appendix B: Coding Style 385 Typographic Conventions 385 Use of Spaces 385 if...else...then 386 do...loop 386 begin...while...repeat 387 begin...until...again 387 Block Comments 387 Stack Comments 388 Return Stack Comments 388 Numbers 388 Optimizations 389 Case Insensitivity 389 Index 391 ---------------------------------------------------------------------- PREFACE This manual, "Writing FCode Programs for PCI", is derived from the Sun Microsystems manual "Writing FCode Programs" with adaptations specific to IEEE Standard 1275-1994 and to PCI FCode drivers. Who Should Use This Book This manual is written for designers of PCI interface cards and other devices that use the FCode programming language. It assumes that you have some familiarity with PCI card design requirements and Forth programming. The material covered in this manual is specifically for those developing FCode applications for PCI peripherals. The FCode language is defined by IEEE Standard 1275-1994 Standard for Boot Firmware (hereafter referred to as Open Firmware). This manual also assumes that you have read and understood PCI Local Bus Specification 2.0 (or later) and PCI Bus Binding to IEEE Standard 1275-1994 1.2 (or later). How This Book Is Organized * Chapter 1, "PCI Cards and FCode", introduces the basic relationships between FCode device drivers and the hardware that they control. * Chapter 2, "Elements of FCode Programming", introduces the basic elements of FCode, stack notation, and programming style. * Chapter 3, "Testing FCode Programs", describes the process of producing FCode programs, from source file to testing working programs. * Chapter 4, "Packages", describes the basic units of FCode program function. * Chapter 5, "Properties", describes properties, which define how an FCode device driver program "sees" the hardware that it controls. * Chapter 6, "FCode Basic Concepts", discusses concepts that are common to most or all FCode drivers. * Chapter 7, "Block and Byte Devices", through Chapter 11, "Serial Devices", describe currently-defined device types, programming requirements, and give some examples of device drivers for the various device types. * Chapter 12, "Open Firmware Dictionary", describes currently-defined FCode words, their functions and use, with brief programming examples. * Appendix A, "FCode Reference", lists all currently-defined Fcode words according to functional grouping, name, and byte value. * Appendix B, "Coding Style", contains an Open Firmware coding guideline. Related Books This manual does not pretend to cover everything you need to know to write FCode drivers for PCI cards. You'll have to read some other books, too. For information about PCI and Open Firmware, see the following manuals: * IEEE Standard 1275-1994 Standard for Boot (Initialization Configuration) Firmware, Core Requirements and Practices * PCI Bus Binding to IEEE Standard 1275-1994 1.2 (or later) * PCI Local Bus Specification 2.0 (or later) For more information about Forth and Forth programming, see: * Programming Languages - Forth, American National Standards Institute, Inc. * Forth: A Text and Reference, Mahlon G. Kelly and Nicholas Spies. Prentice Hall. * Starting FORTH, Leo Brody. FORTH, Inc., second edition, 1987. * Forth: The New Model, Jack Woehr. M & T Books, 1992. Code Examples All the sample drivers shown in this manual are available in machine-readable form. Development Tools FirmWorks has available a PCI Developer's Kit that allows PCI drivers to be developed and tested on a 486DX-33 PCI machine running MS-DOS. If you don't have the PCI Developer's Kit and would like more information about it, contact FirmWorks at info@firmworks.com. ---------------------------------------------------------------------- CHAPTER 1: PCI Cards and FCode The Purpose of FCode Each PCI card identifies itself with a set of up to 64, 32-bit "configuration registers". The purpose of these registers is to provide a standard set of descriptive information in a known place. The configuration registers contain data identifying the type of card, its manufacturer and various other characteristics of the card. In addition, a PCI card can have an "expansion ROM" containing additional information such as a BIOS extension for the card or an FCode program. A BIOS extension provides a driver for the card to be used when the card is installed in a system that uses an Intel x86 compatible processor. An FCode program provides, at a minimum, additional descriptive information beyond that provided by the configuration registers and can provide a processor-independent boot-time driver for use in non-x86-based systems. An FCode program can also contain (or can help the operating system to locate) processor-specific and operating system-specific OS drivers. Locating the FCode Program The first 16 PCI configuration registers are collectively known as the configuration address space header. Included within this header is the expansion ROM base address register. If Bit 0 of this register is reset, the PCI card has no expansion ROM. If Bit 0 is set, the PCI card has one or more expansion ROMs whose base address is specified by Bits 11 - 31 of the register. The ROM(s) can contain several different images to accommodate different machine and processor architectures. As shown in Figure 1, each such ROM image has a header record and a PCI Data Structure that together describe the image. The header record is located at the start of the image and contains a pointer to the PCI Data Structure. The PCI Data Structure, in turn, contains a number of fields including: * A code type field This field identifies the type of code contained within the image * An image length field This field defines the length of the image in integral multiples of 512 bytes. * An indicator field This field defines whether there are additional images located after this image. FCode Program Functions If the code type field has a value of 1, the ROM contains an FCode Program that, at a minimum, identifies the device and its characteristics. An FCode Program may also include an optional software driver that lets you use the card as a boot device or a display device during booting. The software driver may also include diagnostic selftest code. In addition to designing hardware, the process of developing PCI devices may include writing, testing, and installing FCode drivers for the device. These drivers, if present, serve three functions: * To exercise the device during development, and to verify its functionality. * To provide the necessary driver to be used by the system boot ROM during power-up. * To provide device configuration information. In practice, these functions overlap substantially. The same code needed by the system boot ROM usually serves to significantly test the device as well. The ROM code is used before and during the boot sequence. After the boot sequence finishes, and while not using the Open Firmware User Interface, most PCI devices are controlled with operating system device drivers. FCode Programs are written in the FCode programming language, which is similar to ANS Forth. FCode is described in more detail in Chapter 2 "Elements of FCode Programming". FCode ROM Format An FCode ROM image is located within the PCI Expansion ROM on a 512 byte boundary. Its size typically ranges from 60 bytes (for a simple card that identifies itself but does not need a driver) to 1-4K bytes (for a card with a simple boot driver) to 10K bytes (for a device with a complex boot driver). It is good practice to make FCode boot drivers as short as is practical. An FCode ROM image for PCI is organized as follows: * Header (26 bytes: consisting of ROM signature and a pointer to the associated PCI Data Structure) * PCI Data Structure (24 bytes: See the PCI Local Bus Specification for details) * Body (FCode program; 0 or more bytes). * End Token (either End0, a zero byte, or End1, an alternative all 1's byte). Interpreting FCode For each PCI slot containing a card, the following process is followed during boot-up to find and interpret any FCode programs: * Scan all slots in numerical order. * For each slot read the header type field. * If the header field type indicates a multi-function device, perform the following sequence for each function that is present. * Otherwise, perform the following sequence for the card's Function 0. * Create a number of properties from the information contained in the PCI configuration registers. (See the PCI Bus Binding to IEEE Standard 1275-1994 for the details.) * Determine whether the device contains an expansion ROM and, if so, whether that ROM contains an image containing an FCode program. * If an FCode program is present, copy the FCode program into RAM and evaluate it * If the function does not have an FCode program: * Create the "reg" and "name" properties from the information in the PCI configuration registers. * If possible, create the "power-consumption" property from the state of the PRSNT1# and PRSNT2# connector. * Disable fixed address response by clearing the PCI configuration address header's command register. * Enable Memory Space response by setting Bit 1 in the command register. Device Identification An FCode ROM must identify its device. This identification must include, at a minimum, the device name. Identification information may include additional characteristics of the device for the benefit of the operating system and the CPU boot ROM. The CPU's FCode interpreter stores each device's identification information in a device tree that has a node for each device. Each device node has a property list that identifies and describes the device. The property list is created as a result of interpreting the program in the FCode ROM. Each property has a name and a value. The name is a string and the value is an array of bytes, which may encode strings, numbers, and various other data types. See Chapter 5 "Properties" for more information. Creating and Executing FCode Definitions Many FCode programs create executable routines, called methods, that typically read from and write to device locations to control device functions. These definitions are also stored in the device tree node for that device. Once defined, these routines may be executed under any of the following circumstances: * Interactively through the Open Firmware User Interface's ok prompt (for selftest or other purposes). * By the Open Firmware Client Interface (for using this boot or display during system start-up). * Automatically by the Open Firmware Device Interface during FCode interpretation (for power-on initialization or other purposes). ---------------------------------------------------------------------- CHAPTER 7: Display Devices The display device type applies to framebuffers and other devices that appear to be memory to the processor with associated hardware to convert the memory image to a visual display. Display devices can be used as console output devices. Required Methods The display device's FCode must declare the display device type, and must implement the methods open and close. System defer words are loaded by appropriate routines. is-install, is-remove and is-selftest are used to create the open, close and selftest routines. set-font initializes the values of frame-buffer-adr, char-height and char-width, all of which are built into the system ROM and can be used by any display device driver. For display devices, created methods interact with Open Firmware commands in a way that is different from that of other device types. Other device types provide methods that are found by dictionary searches looking for specific names. Some FCodes are specifically designed for display devices. See Table 68 through Table 74 in Appendix A, "FCode Reference". Required Properties Table 22 Required Display Device Properties ---------------------------------------------------------------------- Property Name Typical Value ---------------------------------------------------------------------- name " INTL,cgsix" ---------------------------------------------------------------------- reg list of registers {device dependent} ---------------------------------------------------------------------- device_type " display" {required for display devices} ---------------------------------------------------------------------- Device Driver Examples Simple Display Device Driver This is a sample FCode program for a display device that does not need to be usable as a console display device during system power-up. Code Example 7-1 Basic Display Device Driver Example \ Basic display device driver hex " INTL,cgsix" name " INTL,501-xxxx" model h# 20.0000 constant dac-offset h# 10 constant /dac h# 30.0000 constant fhc-offset h# 10 constant /fhc h# 30.1800 constant thc-offset h# 20 constant /thc h# 70.0000 constant fbc-offset h# 10 constant /fbc h# 70.1000 constant tec-offset h# 10 constant /tec h# 80.0000 constant fb-offset h# 10.0000 constant /frame : >reg-spec ( offset size -- encoded-reg ) >r 0 my-address d+ my-space encode-phys 0 encode-int encode+ r> encode-int encode+ ; 0 0 >reg-spec \ Configuration space registers dac-offset /dac >reg-spec encode+ fhc-offset /fhc >reg-spec encode+ thc-offset /thc >reg-spec encode+ fbc-offset /fbc >reg-spec encode+ tec-offset /tec >reg-spec encode+ fb-offset /frame >reg-spec encode+ " reg" property end0 ---------------------------------------------------------------------- CHAPTER 11: Open Firmware Dictionary This dictionary describes all of the words defined by IEEE Standard 1275-1994. Included within this dictionary are all of the pre-defined FCode words that you can use as part of FCode source code programs. Appendix A, "FCode Reference", contains a command summary, with words grouped by function. The dictionary also includes assembler directives, debugger commands, tokenizer directives and macros, configuration variables, properties, standard methods, nvedit commands, Client Interface commands and User Interface commands. The words are given alphabetically in this chapter, sorted by the first alphabetic character in the word's name. For example, the words mod and */mod are adjacent to each other. Words having no alphabetic characters in their names are placed at the beginning of the chapter, in ASCII order. The boot ROM and tokenizer are case-insensitive (all Forth words are converted to lowercase internally). The only exceptions are literal text, such as text inside " strings and text arguments to the ascii command, which are left in the original form. In general, you may use either uppercase or lowercase. By convention, Open Firmware drivers are written in lowercase. All arithmetic uses 32-bit signed values, unless otherwise specified. Defining words create a header by calling external-token, named-token, or new-token. See the definitions of these words for more details. All FCode byte values listed in this chapter are given in hexadecimal. The dictionary definitions have the following form: name "pronunciation" stack: ( stack diagram ) code: FCode# generates: tokenizer macro (if applicable) Prose description. ! "store" stack: ( x a-addr -- ) code: 72 Stores x at a-addr. For more portable code, use l! if you explicitly want a 32-bit access. a-addr must be aligned as given by variable. See also: c!, w!, l!, rb!, rw!, rl! " "quote" stack: ( [text<">< >] -- text-str text-len ) code: none generates: b(") len-byte xx-byte - xx-byte Gathers the immediately following text string or hex data until reaching the terminator ". At execution time, the address and length of the string is left on the stack. For example: " AAPL,new-model" encode-string " model" property You can embed control characters and 8-bit binary numbers within strings. This is similar in principle to the \n convention in C, but syntactically tuned for Forth. This feature applies to the string arguments of the words " and ." The escape character is `"'. Here is the list of escape sequences: Table 29 Escape Sequences in Text Strings ----------------------------------------------------------- Syntax Function ----------------------------------------------------------- "" quote (") ----------------------------------------------------------- "n newline ----------------------------------------------------------- "r carriage return ----------------------------------------------------------- "t tab ----------------------------------------------------------- "f formfeed ----------------------------------------------------------- "l linefeed ----------------------------------------------------------- "b backspace ----------------------------------------------------------- "! bell ----------------------------------------------------------- "^x control x, where x is any printable character ----------------------------------------------------------- "(hh hh) Sequence of bytes, one byte for each pair of hex digits hh . Non-hex characters will be ignored ----------------------------------------------------------- " followed by any other printable character not mentioned above is equivalent to that character. "( means to start parsing pairs of hexadecimal digits as one or more 8-bit characters in the range 0x00 through 0xFF, delimited by a trailing ) and ignoring non-hexadecimal digits between pairs of hexadecimal digits. Both uppercase and lowercase hexadecimal digits are recognized. Since non-hex characters (such as space or comma) are ignored between "( and ), these characters make useful delimiters. (The "makearray" tool can be used in conjunction with this syntax to easily incorporate large binary data fields into any FCode Program.) Any characters thus recognized are appended to any previous text in the string being assembled. After the ) is recognized, text assembly continues until a trailing ". For example: " This is "(01 32,8e)abc"nA test! "!"! abcd""hijk"^bl" ^^^^^^^^ ^ ^ ^ ^ ^ 3 bytes newline 2 bells " control b Note - The use of "n for line breaks is discouraged. The preferred method is to use cr, rather than embedding the line break character inside a string. Use of cr results in more accurate display formatting, because Forth updates its internal line counter when cr is executed. When " is used outside a colon definition, only two interpreted strings of up to 80 characters each can be assembled concurrently. This limitation does not apply in colon definitions. See also: b(") # stack: ( ud1 -- ud2 ) code: C7 Converts a digit ud1 in pictured numeric output conversion. Typically used between <# and #>. #> stack: ( ud -- str len ) code: C9 Ends pictured numeric output conversion. str is the address of the resulting output array. len is the number of characters in the output array. str and len together are suitable for type. See (.) and (u.) for typical usages. ' "tick" stack: ( "old-name< >" -- xt ) code: none generates: b(') old-FCode# Generates the execution token (xt) of the word immediately following ' in the input stream. ' should only be used outside of definitions. See b('), ['] for more details. For example: defer opt-word ( -- ) ' noop is opt-word ---------------------------------------------------------------------- Copyright (c) 1995-1998 FirmWorks. All rights reserved.