I've designed a PDP8 computer and RK08 disk drive controller. I'm trying to test the RK08 controller with a simulated RK05 disk drive. The RK05 disk drive essentially has 6496 sectors and each sector has 256 (16 bit, 4 bits unused) words. All disk operations occurs on sectors: a sector can be read or written.
I have a 3,325,952 byte disk image that has the OS image for testing.
I'm trying to write a testbench in VHDL that simulates the RK05 disk drive using file IO. I don't do a lot of file IO in VHDL and I'm struggling.
As far as I can see:
VHDL seems to preclude opening a file for read and write, (or modification). VHDL doesn't seem to have an easy way to 'seek' through a file.
Verilog has $fseek() which seems like it would significantly simplify everything. I'm not sure if Verilog can open a file for read/write/update, either.
I *could* re-do the testbench in Verilog if doing this in VHDL was impossible but:
- I'm much less competent in Verilog.
- I'd have to give up having a nice PDP8 simulator executable generated by GHDL.
One suggestion would be to reduce the seek times by using multiple smaller files ... 6496 would be way too many; how many tracks on that drive? That suggests loading/modifying/saving an entire track (file) when you need write access. It'll be plenty fast enough to simulate PDP11 file I/O!
A sconversion program between the whole disk image and a directory full of tracks should be easy, even in VHDL.
If that's a binary image, be aware that binary file formats are not well defined in VHDL; the most you can be certain of is that you can write and read back in the same version of the same simulator. Modelsim seems to read commercial binary files in the expected (little-endian) manner.
However Xilinx ISIM (a) assumes opposite-endian-ness to Modelsim, and insists on adding an undocumented header (which varies between ISIM versions) on file output, and expects the same on file input. You can work around this (on Linux) using "head" and "tail" to extract header and data from an ISIM output file, and "cat" to join a previously extracted header to a standard binary file so that ISIM can read it, but it's tedious, unnecessary, and Xilinx refuse to document the process or fix it. Start by writing some known data in ISIM, and look at the file in a hex editor.
I have no experience with GHDL and binary file I/O (or with GHDL at all)
Alternatively it might be easier to be extravagant with disk space, and make the converter translate into a textual format (e.g. hex) for portable VHDL.
Just do the file IO once at startup: read the whole file into an array of integers within your disk drive model. Close that file and then operate directly on the array.
If desired, provide a "write it back to disk" function which writes the whole array back out all at once - to the same file, or somewhere else.
You might find simulations quicker if you make the big array a variable within a process rather than a signal, but if you only write to it rarely, it might not make much difference.
Finally, reading binary can be a pain in VHDL, some simulators behave differently to others. It can sometimes be easier to convert the binary file to a huge list of the same numbers but rendered in ASCII.
TRW Conekt - Consultancy in Engineering, Knowledge and Technology
I was worried that it might take a while to read the entire disk image into the 'big array' but it seemed like it was the simplest approach and it made seeking around the disk simple also.
I decided to write a test program to see what kind of performance this approach might achieve. If it is too slow, I'll try "Plan B".
I'm going to put this off until later. That way I can't mung up the disk image.
Both Xilinx ISE and GHDL hang if the 'big array' is signal. I had to reboot the PC to get control back from ISE... The mouse barely moved, the disk drive lite was on continuously, and I couldn't even get into Task Manager to kill ISE. I manually aborted GHDL after a while.
It looks like I *can* use a shared variable for the 'big array' - that way I can access the 'big array' from multiple processes. Again, this works for both ISE and GHDL - and the performance is pretty good.
The disk image is little-endian. I read the disk image a character at at time and built the 16-bit "words" properly. I believe this to be portable to different simulators. The disk image, however, is probably not portable to big-endian systems.
I really really appreciate the assistance and good ideas from everyone. The PDP8 thing isn't my day job.
Just a minor plug for GHDL - I can compile the PDP8 VHDL code with GHDL and get a 'stand-alone' PDP-8 simulator executable. For example, I used to have an executable that had the RAM preloaded with FOCAL-69. I can synthesize the same code with Xilinx XST and load it into the target hardware.
-------------------------- test program ----------------------
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.std_logic_textio.all; use std.textio.all;
entity test_disk is end test_disk;
architecture behav of test_disk is
subtype word_t is std_logic_vector(0 to 15); type image_t is array(0 to 1662976) of word_t; file OUTFILE : text is out "STD_OUTPUT"; type imageFILE_t is file of character; file imageFILE : imageFILE_t; shared variable image : image_t; shared variable size : integer := 0;
process variable c : character; variable lin : line; variable word : word_t; begin write(lin, string'("Reading Disk Image...")); writeline(OUTFILE, lin); file_open(imageFILE, "diskpack.rk05", read_mode); while not endfile(imageFILE) loop read(imageFILE, c); word(8 to 15) := std_logic_vector(to_unsigned( character'pos(c), 8)); read(imageFILE, c); word(0 to 7) := std_logic_vector(to_unsigned( character'pos(c), 8)); image(size) := word; size := size + 1; end loop; write(lin, string'("Done Reading Disk Image.")); writeline(OUTFILE, lin); write(lin, string'("Read ")); write(lin, size * 2); write(lin, string'(" bytes")); writeline(OUTFILE, lin); wait; end process;
process variable index : integer := 10; variable lin : line; begin write(lin, string'("----> ")); hwrite(lin, image(index)); writeline(OUTFILE, lin); wait; end process;
------------------------------- end test program ---------------------
You are using a protected type for your shared variable aren't you? If not, you are asking for problems from race-conditions. Your code below is probably going to work alright, but it you start reading and writing to the shared variable from multiple processes you are likely to find life gets annoyingly interesting...
Either keep your variable to a single process and create a RAM-like model with address and data lines and control signals, or create a protected type with read and write functions to mediate access to it. The latter might not work in ISE. I think GHDL supports protected types.
Both of those options have well-defined access semantics.
One problem I have had in the past (with GHDL IIRC) was that an EOF character within the file caused it to report end-of-file and return no more data. And the ISE simulator (at one point, not sure if it still does) required a special header on binary files before it would read them.
But fun, right :)
I'd recommend against -ieee=synopsys, especially given you are using numeric_std already.
I don't think there's any guarantee that the process which prints the 7F will run after the file is read. It might depend on the order the processes come in the source file, or it might be completely random.
TRW Conekt - Consultancy in Engineering, Knowledge and Technology