CM 59401M

CURIOUS•MARC'S HP-IB SNIFFER AND PARITY GENERATOR

FPGA HP-IB Parity Generator

While connecting the HP-85 to the HP 7970 tape drive, I discovered that the HP-85 interface does not generate parity correctly. But the HP 7970E requires and checks for odd parity, and worse, will hang up the bus if it detects an even parity command word, even if not addressed to it. I first worked around it by sending pre-calculated raw command codes with the correct parity, instead of using the HP-85 baked-in command mnemonics. But this only works if the HP 7970 is the only device on the bus. For the ASCII art demo video, I also had the printer on the bus. The problem is that the HP-IB printer commands are automatically generated by the HP-85 internal ROM, don't have the correct parity, and as soon as the HP 7970 sees them, even though the commands are not addressed to it, it will think it's an error and hang the whole bus. So I fixed the problem by building a simple FPGA-based device that corrects parity on the fly.

Meet the CM 59401M

The CuriousMarc CM 59401M instrument is inspired by the Hewlett-Packard HP 59401A, an early HP-IB logic analyzer instrument that displays or generates HP-IB bus data and control lines. You can see how the original HP 59401A works in this video here, it's a lot of fun to paddle in the HP-IB bits one by one.

My CuriousMarc (CM™) version of the device also shows the codes and status lines, but will correct the bus parity on the fly, so it is always odd during commands and therefore acceptable to the HP 7970E. It will also display which current code is on the HP-IB data lines, either in Octal (if switch 8 on the board is on) or hexadecimal (switch 8 off). When the CM59401 has to intervene to fix parity, dots are illuminated at the bottom of the display. In addition, the LEDs show the state of the HP-IB status lines. LEDs are on when lines are active (i.e. low)., following the chart below.

D1 D2 D3 D4 D5 D6 D7 D8

DAV NRFD SRQ NDAC REN IFC ATN EOI

It is built from a Numato Lab Elbert v2 FPGA board, which has a Spartan 3A Xilinx chip on-board. The documentation for the Elbert V2 board is available below.

The board is connected to a GPIB female connector. I used a vintage HP 10342 HP-IB probe board tucked underneath to make a clean interface, using it as a breakout.

The GPIB pins connect to the Elbert V2 connectors P1 and P6 according to the diagram below. I had to swap connections from P1 to P6 to use the DAV as clock, as P6 is the only connector with the FPGA clock inputs. Trust the blue numbers on top of the connectors. The pins of the FPGA board are named in black, while the corresponding GPIB signals from the breakout board are in blue.

The FPGA image and the download tools are below. The USB driver needs to be installed to connect to the board, then use the ElbertV2Config tool to upload the hpib_paritygen.bin binary to the board.

The source code and project settings for the FPGA project is in the folder below. I used Xilinx ISE Design Suite as the development environment. The project file is the .xise file. There are three sources files for the code itself:

  • clock_gen.v: the 100 MHz clock, auto-generated by the Xilinx macro

  • SevenSegmentDisplay.v: the driver for the 3 digit display

  • HPIB_ParityGen_Top05.v: the actual parity generation code

  • The .pdf files are my handwritten notes taken during the program development.

The .ucf file is the IO mapping. The .html files are for information, and contain the project settings and synthesis results summary.

Below is the most important line of code for the parity generator. Parity is changed by pulling the D8 bit line low (i.e. active, as all HP-IB lines are active low):

assign Parity = ^hpib_data[6:0] || ~EOI || ATN;

It simply keeps calculating parity correction on the fly using simple combinatorial logic. Mixing in EOI and ATN signals makes sure that parity correction only happens when an HP-IB command is sent, but not on regular data words, which can be any 8 bit combination.

`timescale 1ns / 1ps//////////////////////////////////////////////////////////////////////////////////// Company: // Engineer: // // Create Date: 01:46:37 01/13/2016 // Design Name: // Module Name: HPIB_ParityGen_Top // Project Name: // Target Devices: // Tool versions: // Description: //// Dependencies: //// Revision: // Revision 0.01 - File Created// Additional Comments: ////////////////////////////////////////////////////////////////////////////////////module HPIB_ParityGen_Top( input Clk, // hardware clock pin input DPSwitch8, // hardware switch (active low) input [6:0] DIO, // HP-IB data lines 1 t0 7 input DAV, // HP-IB Control lines input NRFD, input SRQ, input NDAC, input REN, input IFC, input ATN, input EOI, output Parity, // HP-IB data line 8, Parity output [7:0] SevenSegment, // hardware LED segments (active low) output [2:0] Enable, // hardware LED anodes enable (active low) output [7:0] LED // active high? ); // Master clock module: 100 MHz clock wire clk_100Mhz; clock_gen c1 (.CLK_IN(Clk), .RST_IN(1'b0), .CLK_OUT(clk_100Mhz));
reg [3:0] digit0, digit1, digit2; reg [2:0] dot, blank; reg parity_reg; wire [11:0] hpib_data; // internal bus to connect IO signals from input
// connect LEDs directly to the HP-IB lines with an inverter // LED are active high, HP-IB control lines are active low // LED are reverse numbered on the board, LED[0] is D8 // D1 D2 D3 D4 D5 D6 D7 D8 assign LED = {~DAV, ~NRFD, ~SRQ, ~NDAC, ~REN, ~IFC, ~ATN, ~EOI}; // connect inverted HP-IB imput data to lower bits of hpib_data assign hpib_data = {5'b00000, ~DIO[6:0]}; // connect the Parity output to a 3-state register //assign Parity = parity_reg; initial begin parity_reg=1'bz; // initial parity is high-z dot=3'b000; end // splice hpib-data bits into digits for hex or octal readout always @(*) begin if(DPSwitch8) begin // switch 8 off/high, display in hex digit0 = hpib_data[3:0]; digit1 = hpib_data[7:4]; digit2 = hpib_data[11:8]; blank = 3'b100; // leftmost digit blank end else begin // switch 8 on/low, display in octal digit0 = {1'b0,hpib_data[2:0]}; digit1 = {1'b0,hpib_data[5:3]}; digit2 = {1'b0,hpib_data[8:6]}; blank = 3'b000; // all digits on end end
// generate parity continously by combitional logic assign Parity = ^hpib_data[6:0] || ~EOI || ATN; // show state of parity generation at DAV always @(negedge DAV) begin if(Parity) dot <= 3'b000; else dot <= 3'b111; end // display result on 7-segment onboard display SevenSegmentDisplay display(clk_100Mhz, digit0, digit1, digit2, dot, blank, SevenSegment, Enable);
endmodule