Firmware for a laser-quality thermal inkjet printer - technical
Mark J. DiVittorioFirmware for a Laser-Quality Thermal Inkjet Printer
THE DEVELOPMENT PROCESS for the firmware in the HP DeskJet printer represents a departure from the traditional firmware development process at Hewlett-Packard's Vancouver Division. Before the DeskJet project, the process for developing firmware for printing products was tolerably simple. All of the firmware was written in assembly language for the host processor of a single dedicated product. When another printer was to be developed (possibly with a different microprocessor) the firmware was totally rewritten and reuse was nil. All the tools used (editors, assemblers, and debuggers) were resident on HP 64000 Logic Development Systems, and other tools were virtually nonexistent.
The feature set of the DeskJet printer matches HP's PCL (Printer Command Language) Level III. Capabilities include printing graphics at 75 to 300 pixels per inch, mixing multiple type styles and sizes on a given page, performing enhancements such as bolding and un derlining, downloading RAM-resident fonts, and printing letter-quality and draft-quality text. This command set had not been implemented previously in a high-volume product at the Vancouver Division. Given the DeskJet printer's ability to print at a very high resolution of 300 dpi and the objective of providing a very high level of formatting, we felt that a new approach was required.
A high-level language, C, was chosen to implement the DeskJet feature set, with the target processor being a Z80 microprocessor. The firmware is basically split into two categories: code that implements the generic feature set, called generic printer code, and code that interacts with the custom electronics and mechanissm, called product specific code. Both segments of the code set are almost entirely written in C, although there is a small amount of assembly language code that performs paper motor control and provides feedback for the servo in the carriage velocity control system. This was necessary because these functions have to be done quickly and in real time.
This separation of generic printer and product specific code allows the generic printer code to be shared with another product, the HP RuggedWriter 480, an impact printer. The resolution, use, and functionality of the two products are quite different, but splitting the code into two parts paves the way not only for code sharing between these products but also for reuse of the generic printer code in products of the future. As an experiment, the generic printer code was ported to a different processor architecture under development. This effort was completed in approximately two months. The majority of defects found in the firmware of new products can now be expected to occur in the product specific code, since the generic printer code has been reused and is in a mature state. In addition, the engineering resources required to do products of the future should be reduced.
The development of the DeskJet printer code was facilitated by an HP 9000 Series 500 computer system. The HP-UX operating system provided access to a number of tools that were otherwise unavailable. This system also allowed updated versions of the working code set to be made available to all the designers for integration of the generic printer code and the printer specific code. Operating in the UNIX environment also allowed extensive use of DTS, an inhouse HP defect tracking system, to track defects as they occurred and were resolved. On the DeskJet project, defects were tracked from the breadboard phase through to manufacturing release. Originally, the system was used to track software defects, but eventually, separate tracking was also done on the electronic and mechanical defects that were found in various phases of testing.
Generic Printer Code
Responsibilities of the generic printer code include parsing of PCL data, PCL level III+ page formatting, dyanmic memory management for the page buffer, and font support. The printer specific code is responsible for any special data formatting required by the print engine, for mechanism control, for management of the keys and lights on the control panel, and for I/O.
Supporting both the DeskJet printer and the RuggedWriter printer with a common set of firmware was a challenge because of the many differences between the two printers. The DeskJet printer's main contributions are high-resolution graphics, powerful font support, and data formatting, while the RuggedWriter printer emphasizes speed and paper handling.
Generic Printer Operating System
The generic printer code is broken up into independent processes such as the parser and the page formatter. Most processes are written such that when they are called they will take some data out of their input buffer, process it, place some data in their output buffer, and return.
Process control is performed by the MCP (master control program) which is a minimal operating system designed to support processes without adding significant execution time overhead.
The MCP maintains a table of active processes. The scheduling algorithm implemented by the MCP simply invokes each process in the table in a round-robin fashion. The MCP has no ability to wrest control from a process that has been invoked. Processes are responsible for volumtarily relinquishing control.
In the normal sequence of events, the MCP calls a process's entry point, the process finds its input data and turns it into output data, and then the process returns control to the MCP. For example, when the parser is called, it attempts to read bytes from the input buffer until it has parsed a string of printing characters or a complete PCL escape sequence. It then sends out a token representing the data and returns to the MCP.
In addition to returning, a process can give up control by calling the MCP function Suspend. The Suspend function saves the state of the process stack and returns to the scheduling loop of the MCP. To reinvoke a suspended process, the scheduler restores the stack of the process and executes what looks like a return from the call to Suspend.
Suspending takes much more time and RAM than returning, so processes are encouraged to return whenever possible and use Suspend sparingly. Most processes suspend for one of two reasons. The first is that they have created some output data but there is insufficient room for it in the output buffer. In this case they must suspend and wait for a downstream process to create room in the output buffer. The other reason is that the input data stream has dried up unexpectedly. For example, the parser will suspend if it runs out of input data in the middle of an escape sequence.
Another feature provided by the MCP is that processes can be dynamically added to and deleted from the process table. This is called process activation and deactivation. For an example of why dynamic process activation is useful in a printer, consider the self-test process, which is responsible for creating the data for a printing self-test. This process is deactivated until the user explicitly requests a printing self-test, at which time it is activated.
The Z80 implementation of the MCP in the DeskJet printer requires approximately 1500 bytes of ROM and 350 bytes of RAM. Approximately 85% of the code is written in C with the other 15% in Z80 assembly language.
Parser
One of the responsibilities of the generic printer firmware is the interpretation of printer commands sent in the form of PCL escape sequences. The parser translates these escape sequences into tokens, which are used by the page formatter to indicate functions to be performed. The parser determines whether an escape sequence is syntactically correct. The parser does not place product specific limits on the values used in the escape sequences. The product specific limits are evaluated by printer specific code at a later time. The parser receives data from a printer specific code function so that the details of I/O and data communication buffering are left under printer specific code control.
Another responsibility of the parser is the handling of graphics data. PCL graphics data is sent in horizontal raster format. The parser must store the data in a manner that facilitates removal of the data for printing in a vertical format. Furthermore, the data may be transferred to the printer in one of three different modes. Two of these modes involve data compaction and the paser must expand the data to the uncompacted form.
Page Formatter
The DeskJet feature set includes many PCL Level III and IV commands for positioning to any arbitrary point on a page. However, because of potential ink-smearing problems, the DeskJet mechanism does not support backing up paper. Because of these two requirements, the generic printer code includes a page formatter, which is responsible for sorting all of the data before printing.
For cost reasons, the DeskJet printer only contains enough RAM to hold about 20% of a typical page, which is fundamentally incompatible with the need for a page formatter. The solution implemented in the DeskJet printer is a RAM-limited page formatter. The firmware is written as a page formatter that attempts to process all of the data on a page before sending off any of the page to be printed. However, if the formatter runs out of memory while processing a page, it sends off the data that is highest on the page (closest to the top) to be printed. The result is that the DeskJet feature set supports backward positioning for applications such as creating superscript characters, but it does not support full, random page addressing. This compromise requires DeskJet drivers to perform most of the vertical sorting of the data before it is sent to the printer, while still allowing features such as superscript characters to be easily accessed.
Data in the page buffer that can be printed with a single pass of the printhead is kept on a linked list, sorted by horizontal position, known as a task. Each task also has a header which is used to link all of the tasks on a page, sorted by vertical position.
Because data is typically received for a page in top-to-bottom, left-to-right order, it was found that the page buffer could be built much faster by keeping the lists sorted in reverse order. thus the lowest task on a page is kept at the head of the task chain and the rightmost data of a task is kept at the head of the task. This organization means that most data is inserted into the page buffer at the head of the first task, which considerably reduces the time spent searching through the buffer for a place to insert the data.
Font Support
PCL classifies fonts by a set of font attributes such as height, pitch, and stroke weight (boldness). Escape sequences sent to the printer can be used to specify desired values for each of the font attributes. Because the user can request any combination of font attribute values at any time, the firmware cannot rely on the fact that the requested font actually exists in the printer.
For example, the user may request a 12-point, 10-pitch font even if the only fonts available are 12-point, 12-pitch and 14-point, 10-pitch. In these cases, PCL specifies that the printer must perform a prioritized closesxt-fit algorithm to select a font. PCL specifies both the priority of the attributes and the rules used to determine closest fit. In the above example, the 12-point, 12-pitch font would be selected because pitch has a higher priority than height.
The generic printer select-font implementation must meet several requirements. The first is that it must handle the large number of fonts that can be stored or generated by the DeskJet printer. A second constraint is that there is no standard set of algorithmic font enhancements. For example, the DeskJet printer can algorithmically generate half-height characters and the RuggedWriter printer can italicize characters. Therefore, the select-font code cannot take advantage of a known set of font enhancements. The final requirement is that the select-font algorithm must be fast. Some printer drivers do foolish things such as force a select-font operation to occur for every character, so time spent in the select-font process can quickly reduce the throughput of the printer.
To satisfy all of these requirements, at power-up a printer specific code function (not part of the generic printer code) builds tables in RAM that describe all of the available fonts. There is one table for each of the font attributes. For example, there is a pitch table and a height table. An attribute table contains one entry for each of the unique values available for that attribute. Along with the attribute value, the entry specifies ID numbers for all of the fonts that can supply that value.
The following are examples of these tables: Algorithmic enhancements: Available fonts: - half-height font 1 - 12-point, 12-pitch - half-width font 2 - 14-point, 10-pitch - double-width font 3 - 7-point, 24-pitch Pitch table: Height table: 5-pitch-font 2 3.5 point-font 3 6-pitch-font 1 6 point-font 1 10-pitch-font 2 7 point-font 2, font 3 12-pitch-font 1, font 3 12 point-font 1 20-pitch-font 2 14 point-font 2 24-pitch-font 1, font 3 48-pitch-font 3
To select a font from these tables, the select-font code first initializes an eligible list with the IDs of all of the fonts in the printer. It then processes the highest-priority attribute by examining all of the available values, choosing the closest match, and then forming a new eligible list by a logical AND of the old eligible list and the list of fonts that provide the chosen value. This process is repeated for each of the font attributes in the order of priority specified by PCL.
The implementation of this algorithm uses bit strings for both the eligible lists and the lists of font IDs in the tables, so the AND step of the algorithm is very fast.
Epson FX-80 Emulation Cartridge
The Epson FX-80 Emulation Cartridge was developed to maximize support of the DeskJet printer on existing software applications.
The DeskJet printer's native escape sequence or command set is HP's PCL (Printer Command Language). Prominent software vendors were preintroduced to the capabilities of the DeskJet printer command set to allow adequate time for them to support the printer before its introduction. There was still a major concern for those users who had previously purchased software or were using applications not targeted for preintroduction support.
Since the HP LaserJet II printer also implements the PCL command set and has the same basic print resolution of 300 dots per inch, an extensive effort was mounted to verify support of the DeskJet printer by software applications that have LaserJet drivers. Information about such support on a large number of software packages, including limitations, has been documented and included within the DeskJet Owner's Manual. There was still, however, a concern for existing applications that do not support either the LaserJet printer or the DeskJet printer.
To support the DeskJet printer on applications without LaserJet DeskJet support, a decision was made to develop a cartridge that provides emulation of an escape sequence standard that has long been in existence and is commonly supported by software applications. Although Epson FX-80 emulation provides support on the multitude of existing applications as intended, it should be noted that the functionality of the FX-80 does not provide optimal support of all the features offered by the DeskJet printer. The FX-80 emulation support strategy is a backup strategy, not an attempt to maximize feature access and control of DeskJet capabilities.
The remainder of this article reviews two key elements in the firmware development of the FX-80 Emulation Cartridge. The first portion discusses how a major challenge, providing graphics aspect ratio compliance between an emulated printer and a target printer of greatly dissimilar base dot densities, was met. The second portion describes how an aggressive development schedule was attained while leveraging from a partitioned high-level-language firmware design that was also under development.
Graphics Emulation
Besides supporting the command set, fonts, and features of the Epson FX-80 printer, it was important for graphics output to be dimensionally correct despite differences in DeskJet and FX-80 resolution. For graphics output, the DeskJet printer is a 300-dot-per-inch (vertically and horizontally) raster device. In contrast, the Epson FX-80 is a column-oriented graphics device with a vertical resolution of 72 dpi and selectable horizontal resolutions of 60, 72, 80, 90, 120, and 240 dpi. Fig. 1 shows the difference between column and raster formats.
Current inkjet products emulating Epson printers support graphics only in their native resolutions. Without some sort of printer driver customization, the resolution differences result in incompatibilities in both size and aspect ratio (see Fig. 2). To attain graphics output from the DeskJet printer that matches the FX-80 printer's using a standard Epson printer driver, incoming graphics data is scaled (to match resolution and aspect ratio) and rotated to DeskJet raster format.
When the resolution of the target device (the DeskJet printer in this case) is finer than that of the emulated device, scaling can be done simply by approximating the emulated dots with multiple target device dots. A simplified example, where the horizontal and vertical resolutions of the emulated system are equal and scaling error is neglected, is shown in Fig. 3. The horizontal or vertical expansion for an emulation dot can be determined by the following generalized equations: d.sub.t = (S.sub.e + S.sub.t./2 - E(n))/S.sub.t E(0) = 0 E(n + 1) = S.sub.t.d.sub.t - S.sub.e d.sub.t is the number of target system dots to produce. S.sub.t is the number of units per target system dot. S.sub.e is the number of units per emulation dot, which may be different in the horizontal and vertical axes. E(n) is the cumulative scaling error.
As an example, take the case of scaling to an emulation resolution of 72 dpi vertically and 90 pi horizontally. Use a convenient virtual resolution of 10800 units/inch--the least common multiple of the target system resolution and all emulated resolutions--so that only integer arithmetic is used. Thus, S.sub.t = 10800/300 = 36 units/dot, and S.sub.e = 10800/90 = 120 units/dot for the horizontal expansion or S.sub.e = 10800/72 = 150 units/dot for the vertical expansion. Given this, each column of graphics can be expanded as it is received using the generalized scaling equations above.
In practice, it is desirable to eliminate the use of multiply and divide instructions to increase speed, which is critical when handling large amounts of graphics data. Fortunately, the equations can easily be reduced to algorithms requiring only addition and subtraction. The number of target dots on one axis for an emulation dot, d.sub.t., is determined by repetitively adding S.sub.t to the constant S.sub.t./2 - E(n) until the result exceeds S.sub.e. A target dot is output at each addition step. The next error term, E(n+1), is simply the result of the additions minus S.sub.e.
Continuing with the example, Fig. 4 shows the result of converting two rows of column graphics to DeskJet resolution and raster format.
For throughput enhancement, the emulation takes advantage of the fact that the vertical scaling is just duplication of the raster rows. Dots are not scaled vertically until the complete graphics line has been scaled horizontally. The reason for this is that once the horizontal scaling is done, the vertical scaling is simply a matter of copying the raster rows a given number of times, which is much faster than bit-by-bit scaling. This cannot be done with the horizontal scaling since scaled dots can cross byte boundaries horizontally. Another speedup involves trapping out white space--dot positions that are unused. Since the raster buffer is always cleared before it is used, blank emulation dots or whole columns do not have to be scaled into the buffer; only the position for the next dot or column needs to be calculated. Throughput is increased for any image that has at least 5% white space. The more white space, the greater the throughput gain.
The emulation allows users to replace the built-in-characters with characters of their own design. User-defined characters are downloaded by specifying each column of the character, in the same manner as column graphics data is sent. Because the download data is sent assuming Epson FX-80 resolution, it must be scaled before printing, the same way graphics is scaled. In fact, any downloaded character is printed as graphics. Pica, elite, and compressed character pitches are produced by changing the horizontal scaling factor. Other enhancements such as bold, expanded, superscript, and subscript characters are added by manipulating the character data before printing.
Mechanism Compensation
Even though the scaling algorithm can map all of the FX-80 resolutions to the DeskJet printer's 300-dpi resolution, there still exists a mismatch between the resolution of the paper and printhead placement in the DeskJet printer (1/300 inch) and the Epson vertical resolution (1/72 inch). Because of this, the mechanism error must be synchronized with the scaling process. This is done by monitoring the page position to detect when vertical positioning will move the paper 1/300 inch too far because of roundoff. When such a move is predicted, the scaling routine produces an extra row to compensate. The table below shows how mechanism and scaling positions are reconciled when scaling columns of eight bits.
Leveraged Emulation Cartridge Design
The Epson emulation cartridge was highly leveraged from the DeskJet PCL code and the RuggedWriter Epson LQ-1000 emulation code. Over 85% of this code is written in C and the software architecture is partitioned into blocks with well-defined interfaces. These factors allowed the Epson cartridge development to meet a very aggressive schedule. Only eight engineer-months were required for software engineering. The total project time was four months.
As mentioned earlier, the DeskJet firmware is partitioned into major blocks. The parser, key panel handler, DIP configuration switch handler, and self-test modules all send tokens to the formatter. The formatter receives these tokens and builds tasks for the task processor. For the Epson emulation cartridge, the LQ-1000 parser was leveraged from the RuggedWriter firmware and the rest of the code was leveraged from the DeskJet firmware. Although the individual token handlers needed to be modified to handle the different parameters and their meanings, the formatter's task management code was kept intact. Since the key handler, dip handler, and self-test modules send PCL tokens, these modules had to be modified to send the corresponding Epson tokens whenever possible, but a few PCL tokens and their handlers had to be preserved to allow for functions that had no analogous Epson function (for example, the draft-mode key on the DeskJet printer).
Monitoring Changes in Leveraged Code
The Epson cartridge was under development in parallel with the DeskJet firmware. Although the DeskJet code was the starting point for the Epson code, many changes had to be made throughout the code to send the appropriate Epson tokens and to perform the proper font selection behavior needed for Epson emulation. Typically a particular routine had several lines of code added or deleted to handle the specific needs of Epson emulation.
While the DeskJet code was being changed to incorporate Epson emulation, the DeskJet code was also being enhanced or changed to fix defects. Some to these changes needed to be incorporated into the Epson version, but some of the changes were PCL specific and had no relevance to the emulation project. To monitor these changes, a tool named difftree was created to make the tracking process semiautomatic.
difftree is a UNIX shell script based on the standard diff utility, which finds differences in specified source files in a directory and all subdirectories and creates a parallel directory structure containing files of the differences. In other words, diff finds differences between all source files in particular directory trees. It also logs situations when a source file or subdirectory is in one directory tree but not the other.
For example, the DeskJet project has a directory structure of: /projects/deskjet/source and the Epson project has a structure of: /projects/fx80/source and the subdirectories of source in both of the projects are largely the same. difftree can be run on the entire source tree or on selected subtrees. In practice, we ran difftree on selected subtrees of the code, since certain modules (e.g., the parser) were totally different. For the sake of this example, however, assume difftree was run on all source trees. A third directory name was supplied to contain the differences. A fourth directory name was supplied to contain a tree of expected or "OK" differences (explained below). Hence, difftree could be run by entering: difftree/projects/deskjet/source/projects/fx80/source/projects/fx80/ diffs/projects/fx80/okdiffs
This creates the directory /projects/fx80/diffs/source which contains all the differences. The diffs tree has files of the same names as the files compared in the source and destination trees. Hence, if deskjet/source/fileA is different from fx80/source/fileA, there will be a file FX80/diffs/source/fileA which contains the differences.
The most useful features of difftree is its okdiffs directory tree, which is passed as a fourth directory tree parameter to difftree. When difftree finds differences between two files it compares these differences with a file of the same name in a corresponding okdiffs directory tree. If there is a file in this okdiffs tree of the same name as the files being compared and if this okdiffs file is identical to the differences just found, the differences file is purged. Hence, if a difference is detected and the reason for the difference is an intended divergence in the code, the difference file is simply moved manually to the okdiffs tree and difftree will no longer create a difference file (until one of the files changes again).
Manual involvement was still required to identify the need for incorporating changes in the DeskJet code into the Epson code, and to determine the best way to make the changes ( add the Epson modifications to the new DeskJet code or the DeskJet modifications to the Epson code). difftree, however, provided a quick semiautomatic way to keep the Epson code up to date with the DeskJet code. During the last two months of the Epson project, difftree was run an average of once per week. Incorporating and testing any changes typically required only half a day of one engineer's time each week.
As long as the directory structure of the Epson code was kept the same as the DeskJet code, difftree performed quite well. At times, for various reasons, the DeskJet code had its directory structure rearranged. This required an analogous manual rearranging of the Epson directory trees to use difftree.
Although difftree is not totally automatic and requires human decision to decide which differences are irrelevant and which differences need to be addressed, it has proved to be an extremely useful tool. In our opinion it is unreasonable to expect a totally automatic solution, since human judgment is required to decide which changes need to be incorporated into leveraged code.
Acknowledgments
The DeskJet software development team also included Martin Hash and Donna May. The authors would like to acknowledge their efforts and patience in the development of the software and their ability to adapt to a major change in the development process.
COPYRIGHT 1988 Hewlett Packard Company
COPYRIGHT 2004 Gale Group