Click here to Skip to main content
15,886,518 members
Articles / Programming Languages / Python

FPGA Hardware Simulation Framework (FPGA_HW_SIM_FWK)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (15 votes)
11 Jan 2023MIT25 min read 21.4K   346   19   8
Simulate hardware containing an FPGA programmed in VHDL interactively
This article presents a hardware simulation tool that allows a real-time "interactive" user experience without the need of actual hardware or expensive licenses.

Image 1

Simulate hardware containing an FPGA programmed in VHDL interactively!

In this article, a hardware simulation tool is presented which allows a real-time "interactive" user experience without the need of actual hardware or expensive licenses.

The concurrent simulation of stimulus and results is straight forward, "real" threads can be used to test realistic situations which are otherwise not possible with standard VHDL simulation tools.

This tool is, in fact, a framework that allows emulation of hardware by simulating digital signals (simulation of analog signals is not supported yet).

The simulation framework replaces the FPGA chip itself, as well as peripheral components and devices.

Simultaneous instantiations with different configurations or variants of the tool may even allow simulation of complex systems composed of several FPGAs, sensors and actuators.

Introduction

While resuming my activities in FPGA development (after a pause of 25 years!), I soon realized that some things had changed a lot while some others remained the same.

On the one side, despite its recent spread into new areas, this technology continues to be quite inaccessible to the hobby developer and still serves mainly niche applications.

On the other side, through new efforts to lower costs and support of open source, this topic has gained popularity in the last few years.

Still, buying a development board to play around and try things out may be a blocking obstacle even for the most enthusiastic hobby developers.

Several questions arise when starting this topic.

Depending on the specific application, the variety of different manufacturers, FPGA families, specific functions and sizes, licences and tool support, may be quite overwhelming.

It is exactly at this point where FPGA Hardware Simulation Framework may provide a suitable intermediate step, specially for those without much experience on the topic and short of budget.

With the help of this tool, you may:

  • reduce costs down to zero by avoiding the need of development boards / hardware prototypes, power supplies, cables, logic analyzers, oscilloscopes, sensors, actuators, expensive licenses, etc.
  • avoid dependency to specific manufacturers
  • gain flexibility by adapting and extending the emulated hardware according to your needs

Amongst other things, this article describes the architecture of the framework, the implementation details, possible applications and the current limitations of this new approach.

Contents

Motivation

Before jumping into the details, let us see why you may need this tool and why you will probably find no alternative solutions.

To understand the "why" of this tool, we first need to know the classical approach in FPGA development.

For background information, you may need to read this: FPGA.

The design of a Field Programmable Gate Array is usually done using a Hardware Description Language (HDL).

The most popular hardware description languages are Verilog and VHDL.

Once you have written your code, you may want to test it on a simulation before you finally program the actual FPGA and do further tests on the real hardware.

This is usually done writing a "testbench" using also HDL.

There, you instantiate your Unit Under Test (UUT), provide some inputs, and evaluate the corresponding outputs.

Both, the testbench and the UUT code, can be run in a simulation tool which is generally provided by the FPGA manufacturer.

If everything is as expected and you have a good test coverage, then chances are high that the hardware will work as well.

Of course, you want to make sure, so you then perform further tests directly on the real FPGA.

But, what happens if there is no hardware available? Can you trust the simulation? Have you covered all important test scenarios good enough?

Is it possible instead to use an "enhanced" simulation tool that allows a more realistic and interactive view of the system including the interaction of your logic with the peripherals?

After some research, the answer to all these questions seems to be "no"!

(Go to Contents.)

Simulation and Testing

Usually, writing and running a testbench is all you need before you program the FPGA. But, can you do more?

Actually yes, by using definitions and variables in your testbench, you are able to re-compile and re-run it using different values which may represent different test scenarios.

With this simple approach, you are now able to extend your test coverage pretty easily without re-writing the entire code of the testbench each time.

The next possibility is to read the "stimulus" directly from a file containing data which is used in the testbench. The results may then be stored in another file which can be evaluated externally.

This process is still quite static and involves re-running the testbench every time with a different set of values.

A more flexible possibility is using TCL. This really extends your test capabilities up to a professional level, and even "interactive" changes during test-run are possible.

Yet another possibility would be to use stdin/stdout, although this may not work in some environments or tools.

The possibilities described above have some disadvantages though.

Some are insufficient to provide an interactive experience while others are too complex and still too low-level for a human to understand directly.

For instance, the tester may interactively enter ones and zeros "by hand" to indicate, e.g., the input states of different buttons, but the overall picture is probably only in his head.

Any advanced text-based interface requires intermediate format conversions which increase the complexity and the probability of errors.

Only the final step, where the tester actually presses the corresponding buttons and changes the desired switches and signals on the real hardware, will show, e.g., if the correct LEDs are on or off as expected.

This final "visual verification" and high level interaction with the actual hardware is of vital importance.

In addition, most of the FPGA vendors offer "on-chip debugging" to alleviate the complexity of the verification process. This allows real-time capture of internal nodes in your design.

So fine, we are done right? We have all we need for a complete FPGA development using VHDL.

Well, not quite. If you consider situations where you (still) don't have the hardware, a new possibility is required which involves emulating (simulating) the hardware in some form.

Emulating the hardware does not only allow an interactive experience at FPGA level, but this may also be extended to simulate/emulate external peripheral components such as sensors and actuators.

A nice GUI may even hide the FPGA and hardware details completely to only show the product that the end-user interacts with. In the background, the FPGA logic will run as if we had the hardware inside!

(Go to Contents.)

Architecture Overview

In the previous chapter, several possibilities for simulation and testing of FPGAs programmed in HDL were presented.

At the end, we noticed the need for hardware emulation in some situations.

This chapter derives the rough architecture of a simulation tool that I called FPGA Hardware Simulation Framework that fulfills that goal.

The tool may be considered to be a framework because it is not possible to cover all possibilities in advance through configuration alone.

Instead, the code provided can be easily extended in a pre-defined manner in order to support new interfaces.

Besides Python, the tool described in this article makes use of VHDL, but porting the code to Verilog or using a tool like iverilog to transform VHDL code to Verilog is relatively simple.

Therefore, from now on, we will only mention VHDL, being aware that this is also applicable to Verilog or another Hardware Description Language.

Let us summarize the different possibilities for simulation of FPGAs:

  1. Testbench (TB)
  2. TB definitions and variables
  3. Stimulus and result files
  4. TCL
  5. stdin / stdout
  6. On-chip debugging

All points listed above need tool support by the vendor and a means to input/output simulation parameters. This is shown next:

  • Simulator <-> VHDL (1, 2)
  • Simulator <-> VHDL <-> File (3)
  • Simulator <-> TCL script [ <-> File ] (4)
  • Simulator <-> VHDL <- standard streams -> system console / named pipes (file) (5)
  • Debugger <-> Debugger-Hardware in FPGA <-> FPGA Hardware (6)

In order to implement the FPGA Hardware Simulation Framework, a flexible, simple and open interface is required which is always available to developers and users.

From the options above, the natural choice is "file sharing" / "file io", where the mere existence of the "stimulus and result files" represent a digital signal in some specific state, say ON/OFF or HIGH/LOW.

In order to keep things simple, the current implementation of FPGA_HW_SIM_FWK avoids passing information "inside" the files, which would require additional formatting and checking.

Instead, by convention, the presence or absence of a file with a specific name is all we need to evaluate or force in order for this interface to work.

By emulating digital signals as files one-to-one, a simple yet flexible and powerful interface is defined that can be easily implemented externally, e.g., in a Python GUI-Application as shown later in this article.

The following figure provides an overview of the architecture:

Image 2

Figure 1: Architecture Overview

Tests with up to 64 digital inputs, 64 digital outputs, 64 LEDs, 64 buttons and 64 switches (that is a total of 320 files) worked fine with a clock period of 10 milliseconds.

Although not yet implemented, the architecture overview already shows the possibility to exchange information between FWK_GUI and Testbench.

This may allow, for example, passing additional information relevant for simulation which is not actually required in the UUT.

Note also that it is perfectly possible to drive the simulation simultaneously from FWK_GUI and from the Testbench. Nevertheless, this shall be done with care.

The current framework provides as an example the constant SIMULATE_BUTTON_IN_TESTBENCH that allows selecting the source of simulation for buttons.

This can be set in the file hw_sim_fwk_tb.vhd which is the Testbench of the VHDL project.

(Go to Contents.)

Detailed Design

As shown in Figure 1, the framework consists of two main parts.

The GUI Application programmed in Python serves as an external simulator, providing emulation of synchronous and asynchronous signals via shared files.

The Framework Input and Output programmed in VHDL map file sharing to external signals, providing the "illusion" that they come from the actual hardware!

The Python code has a class called MainWindow with 2 registers (FPGA board, Settings).

FPGA board contains all widgets that present a quite realistic view of a customizable FPGA Development Board.

The user can interact with it by pressing buttons, toggling switches and observing LEDs and states of digital inputs and digital outputs.

With the help of corresponding buttons, the simulation can be started, paused and resumed as required. Also several parameters can be changed if needed (see Figure 3).

The thread thread_scheduler handles all "synchronous" inputs and outputs while other threads like thread_di, thread_switch and thread_button manage "asynchronous" signals.

Note that there is a separate thread for each "asynchronous" signal, even for each individual asynchronous digital input.

The clock signal is toggled in the thread_scheduler between HIGH and LOW at a configured rate. This clock period defines the speed at which we run the VHDL simulation.

In the VHDL Framework Input, we have the module hw_sim_fwk_clock.vhd where a component is defined to "poll" the clock files and set the corresponding signal.

Setting the clock period in the GUI to 1 millisecond or 10 milliseconds works perfectly fine, but in most of the cases, there is no reason to set the clock period so low, also 50ms is fast enough.

Even 1 or 10 seconds may be  better in those cases you want to take a closer look at the simulation, say by watching it in "slow motion".

Then you are able to pause the simulation at the right time and resume after analysis or after changing some parameters. Therefore, this parameter is configurable and can be changed at any time.

Other signals work in a simliar way. Note that the tool comes with several "examples" that are there just for demonstration purposes. Feel free to adapt the code according to your needs.

Let's continue with the VHDL code.

All .vhd files with prefix "hw_sim_fwk" are non-synthesizable. This just means that it is not intended to program this logic into the FPGA.

These modules are there only for simulation purposes.

In fact, with exception of the testbench, all such modules are defined to be part of the library that I called "hw_sim_fwk".

This library is included at the top of the testbench with the following code:

C++
library hw_sim_fwk;
use hw_sim_fwk.all;

On the other side, all modules in the UUT are synthesizable and shall follow some rules for this to be possible.

For instance, you find the top_module entity to be the top module of the UUT. All other synthesizable modules lay below this module.

The code provides several configuration possibilities so you can easily emulate the exact hardware you actually need.

For example, the number of buttons and LEDs can be easily adapted.

In the next figure, you find an RTL (register-transfer level) schematic of an instance of the demo UUT with 2 inputs and 2 outputs:

Image 3

Figure 2: RTL Schematic of UUT (example 2 inputs, 2 outputs)

This logic is generated / synthesized out of the VHDL code in your UUT.

Note that the complexity of the schematic increases dramatically as you add more functions. This is the reason why FPGAs are no longer designed using schematic design tools and HDL is used instead.

Code

Similar to my other articles, I will not go here into the details of the code.

As a result of the "extreme programming" method applied, the beginner level in VHDL, and the "C-like" or "anti-pythonic ?" programming style, I will not dare to put the focus on the code itself.

The code is free and open source for everyone to read and use. Make your own conclusions and do some improvements if required.

But most importantly, see the code as a means to achieve your goals. If you have the time, the talent and a good reason to write an excellent code, then do it. This article may serve as a starting point.

In this case, I just offer a "proof of concept" which works really well and can definitely help other people under different circumstances (see Use Cases).

The code of the Application GUI is written in Python and is compatible with Python versions 3.8 and 3.9.

The set of GUI widgets is based on PyQt.

Several tests have been made on Linux Mint 20 running the python application in PyCharm, in eric6 and as an executable file together with ISE Simulator (ISim).

The executable file in Windows 11 has been generated successfully and seems to have all functionality, but it could not yet be tested together with an FPGA simulation tool.

The current version of the code in GitHub is labelled as 1.0.0.

At the top of this article, you find the link to the project in GitHub and also a .zip file is provided for download.

With help of the batch file gen_exe.bat, an executable file for Windows can be generated using pyinstaller and other tools.

Similarly, gen_exe.sh generates an executable file for Linux. See Generation of Executable File for more details.

(Go to Contents.)

Application GUI

The Python GUI Application consists of 2 registers, named FPGA board and Settings.

Image 4

Figure 3: GUI (FPGA application board)

In future versions, sensors and serial interfaces, as well as a seven-segment display will be added in the empty space around the electronic board.

Even specific product look and feel above the level of the electronic components can be added to bring the simulation experience closer to the end-user.

Image 5

Figure 4: GUI (settings)

(Go to Contents.)

Features

The simulation framework has the following features:

  1. Step by step execution
  2. Pause and resume
  3. Timed execution
  4. Set Reset signal to high / low
  5. Push/toggle buttons
  6. Toggle switches
  7. Display values of digital inputs and outputs
  8. Display LEDs
  9. Plot (not yet implemented)
  10. Sound
  11. Display simulation status (LED and progress stick)
  12. Customizable hardware and simulation (see config.ini and Settings)
  13. Log to .csv file (logging level configurable)

Image 6

Figure 5: CSV log

Let's take a closer look at the last feature.

The .csv files will let you open your data even if you don't have the vendor specific tools.

When comparing the signals shown in the VHDL simulation and in the .csv file, you may notice some small delays within a clock cycle between them, this is perfectly fine.

If you analyze the signals, you will notice that these small delays lay within the simulation tolerances and correspond with the latencies introduced by the registers in the VHDL logic.

Note that in Figure 5, there are some irregular signals. That simulation contains real errors as it was run as a test "without a scheduler".

The "synchronous" signals were instead simulated with different threads sleeping for "exactly" the same time. But this, of course, was not very accurate.

Small jitters were introduced which led to incorrect results, even when taking into account the time spent within the individual threads.

Note that this "bad implementation" of synchronous signals could be used to simulate the jitter which actually happens in the real system.

Though I would not do it that way, this shows again a new possibility (feature?) which is not offered by normal FPGA simulation tools, although you could simulate asynchronous signals in your testbench too.

(Go to Contents.)

Constraints

As long as the FPGA interface to the peripherals is simple and well documented, we can produce a very accurate simulation to test our HDL code.

The main constraint of this tool is probably the fact that bigger FPGAs may not be properly or completely simulated if we need, e.g., to interface specific hardware functionality.

For instance, newer FPGAs are integrated in the same chip together with processors that communicate at very high speeds and exchange huge amounts of data with the FPGA.

This problem arises as well when interfacing external ASICs which are not easy to simulate due to their complexity, or more accurately, due to the complexity of their interfaces.

That is why this tool is more appropriate for smaller FPGAs with standard interfaces.

In general, constraints may arise when high speeds and big amounts of data are involved.

This is already a problem even when using Testbenches in a normal FPGA simulator, but the problem increases when the goal is to have an "interactive and responsive" simulation.

Problems could appear, for example, when trying to interact from the GUI application with "real hardware". Although this is possible, care shall be taken.

Let us consider, for example, the case where we need to read data out of a sensor and then pass it to the FPGA simulator.

There will be a lower limit, say 1 millisecond, which is defined by the speed we can reliably write and read files between the Python Application and the Framework in the FPGA simulator.

In this case, the clock speed would be limited to a maximum of 1 kHz. That is, we could then transmit serially a maximum of 1000 bits per second of sensor data to the FPGA simulator.

If the real sensor generates more data, then we would need to discard several samples, build average values, or lower the speed of data acquisition from the sensor.

How much effort we invest in emulating / simulating the hardware around an FPGA will strongly depend on the costs.

If it is cheaper to buy the actual hardware (and the corresponding tool licenses if required), as opposed to develop software to emulate it, then there is no point in doing that.

As a final note, we may remind that some of the current limitations arise from the interface currently used between the GUI-App and the FPGA simulator, which is based on a very special form of file sharing.

If possible, a different interface may be used e.g., to improve speed. Take a look again at the possibilities listed in Architecture Overview.

(Go to Contents.)

Use Cases

I mentioned already what my own motivation was. This tool can help amateurs in their initial steps in FPGA and VHDL/Verilog without the need of hardware and licenses, thus reducing costs.

But there are for sure several other use cases. Here I list some of them:

  • Learning tool for hobby developers
  • Derivation of test scenarios as a previous step before writing the final testbenches
    (e.g., the tester plays around with the emulated hardware in order to find problems in the logic of the UUT. This leads to corresponding tests in the TB)
  • Demo / Showcase for management
  • Demo / Showcase for customers

In the last two cases, the demo can even be done on the PC of your manager or customer, without the need to install additional tools besides e.g., GHDL.

The executable file of the GUI-Demo-App contains already everything required for it to run (e.g., in Windows no additional .dlls are needed).

The delivered software is open source and free of cost, no hardware is required.

(Go to Contents.)

Project Setup

The Python code can be opened in a project created, for example, with PyCharm or with eric6, although with some adaptations eric7 may also work fine.

The working directory shall be the path were most .py files are found. There, you also find the folders dist/ and ui/.

The Python interpreter can be set to the default in your system but I recommend using a separate Python environment for that.

There, you can put all the things you need as described in Generation of Executable File.

The file mainWindow.ui can be edited with QtDesigner. With that tool, you can, for example, add or edit widgets in the GUI.

While in eric6, refreshing the .ui file in the project after edition is straight forward, when using PyCharm, you need to execute the following command (example for Linux):

sudo python3 -m PyQt5.uic.pyuic -x mainWindow.ui -o Ui_mainWindow.py

The VHDL code can be opened in any suitable FPGA IDE, provided e.g., by Lattice, Microchip, Xilinx or Intel (Altera). Select there any chip that fits the hardware configuration.

Most FPGA manufacturers provide these tools for free in case you use them for non commercial purposes and resign the more advanced features, but the choice will depend on your specific needs.

The only care you need to take is to put all hw_sim_fwk_*.vhd files in a library for simulation purposes only. The name of the library provided in this article is hw_sim_fwk.

Note that the library assignments can only be done by hand in the project within the FPGA IDE because this is not part of the VHDL specification.

This can be done, e.g., while creating your project using the provided files:

Image 7

Figure 6: Add VHDL source files

In case you change the default configuration in the Python GUI application, say by editing config.ini or modifying and saving the settings in the GUI, make sure to set the same values in the constants defined in hw_sim_fwk_tb.vhd.

I think that's it. There are many other small things you may want to adjust but the setup of projects is usually pretty straight forward.

(Go to Contents.)

Generation of Executable File

When programming in Python, it is possible to pack your application in an executable file. This works for both Linux and Windows.

With help of the batch file gen_exe.bat, an executable file for Windows can be generated using pyinstaller and other tools.

Similarly, gen_exe.sh generates an executable file for Linux.

Some of the steps involved are optional or may need to be adapted, but the generation of an executable file consists more or less of the following steps:

  • Edit the .spec file (e.g., specify the file libportaudio64bit.dll using the complete path and provide the name of the executable file)
  • Create and activate your own Python environment
  • Install or upgrade all needed packages and tools, e.g., with help of the pip tool
  • Update the file requirements.txt containing all the dependencies of your application
  • Install all needed dependencies found in requirements.txt
  • Generate the executable file with pyinstaller and with the .spec file

I highly recommend using a specific Python virtual environment.

For a long time, I refused to work that way and put instead all the stuff in my system, this caused me lots of headaches.

(Go to Contents.)

Tools

Here, you find some tools that might help you in your FPGA journey, at least in my case they did:

(Go to Contents.)

About FPGAs

Since my last experience with FPGAs and CPLDs a long time ago, the number of programmable logic elements in an FPGA chip have increased a lot.

In addition to the classical logic elements, the current solutions make more intensive use of convenience hardware modules inside the FPGA, e.g., for I/O, memory, DSP, ethernet communication.

Besides that, the market seems to have divided in two parts, with the bigger FPGAs mixed with processors and highly complex functions in the same chip on the one side, and smaller FPGA families focusing on low cost and low power consumption on the other side.

The latter may be allocated to companies like Microchip and Lattice, while the former seems to be dominated by companies like Xilinx and Intel (Altera).

Smaller FPGA families may compete with microcontrollers, sometimes being suitable as a replacement which is probably not so affected by obsolescence issues and is easier to port.

When compared with microcontrollers, FPGAs are still slower, more expensive and need more power. But some of the disadvantages may be turned into advantages, e.g., when exploiting the high degree of parallelism in application execution.

(Go to Contents.)

Applications of FPGAs

The main FPGA applications are: medical, video & image processing, telecom & datacom, server & cloud and defence and space.

New areas involve development of deep neural networks, security modules, wireless communications, data mining, cryptography, embedded vision, acceleration of algorithms as support for generic processors.

Although I don't have the authority to claim anything about this field, my intuition tells me that the current boost in this technology will not end anytime soon.

In any case, it is exciting to see how much is going on and how much it is yet to be done in this area.

(Go to Contents.)

About VHDL

As in any programming language, the learning curve may be steeper or flatter depending on your background.

But one of the main things to consider is that when writing VHDL code you are not actually "programming", at least not in the classical way.

What you are doing is designing digital logic circuits. You are describing the structure and behavior of electronic circuits by wiring things together.

The same is, of course, true for any other Hardware Design Language like Verilog.

In many cases, you have to be aware of what the synthesizer will generate based on your code. In some other cases, you have no other choice but to check and probably adapt your code or the synthesizer settings in order to obtain the desired circuit.

There are many pitfalls waiting ahead beyond the horizon, specially for typical "software programmers".

But don't let this discourage you. There are tons of good tutorial videos, books and code examples. This was not the case 25 years ago!

It was not until I wrote FPGA_HW_SIM_FWK that I actually put everything together and really learned some things about VHDL that I had read several times before.

With this tool, you now even have a new alternative, an alternative that lets you play around with a "virtual" hardware while not yet having one.

This is a powerful step and I hope you really enjoy the ride the same way I did!

(Go to Contents.)

Next Steps

The current version of the tool contains the minimum amount of features required for me to dare to call it a "framework".

Soon, I will add several important features that will fill the most important gaps.

The following list contains some of the points in my current TODO list:

  • plot signals
  • real analog sensor (e.g., CPU load)
  • simulated analog sensor, including a widget to control it by hand as an option
  • serial communication (UART, SPI, I2C,..) including widgets, e.g. to pass sensor data to the FPGA simulator
  • seven-segment display
  • technical debts:
    • tests on Windows (problem with access to shared files?)
    • test MessageWindows
    • TODOs in code

(Go to Contents.)

Image and Video Gallery

Here you find a link to a video with a short demo of the tool:

(Go to Contents.)

Summary

The tool presented in this article can and will be improved in the future, but in my personal case, it proved already to be very helpful as it is, supporting my first steps in a new topic which sometimes can be really hard.

The idea is a good contribution to support FPGA and VHDL for the general public, presenting several advantages with regards to existing paid or open source and free solutions.

With this tool, FPGA development is extended once again in the open source community, providing a valuable complement to existent tools and technologies.

When used with care, and being aware of its limitations, FPGA_HW_SIM_FWK is more than just a gadget, and it provides interesting ways to have a more realistic and interactive simulation approach for several use cases.

Points of Interest

What we have here is just the first step, but it shows that with improving technology FPGA development doesn't have to be difficult or expensive.

Check under this link other articles that may interest you (topics: encryption, steganography, data-diodes, network-security):

History

  • 15th April, 2022: Initial version
  • 2nd January, 2023: Added reference to new GitHub project FPGA_HW_SIM_FWK_2 with improved performance

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionGreat Job Pin
Mike Hankey28-Jan-23 13:57
mveMike Hankey28-Jan-23 13:57 
AnswerRe: Great Job Pin
Clark Fieseln28-Jan-23 20:45
Clark Fieseln28-Jan-23 20:45 
GeneralMy vote of 5 Pin
0x01AA23-Apr-22 1:58
mve0x01AA23-Apr-22 1:58 
GeneralRe: My vote of 5 Pin
Clark Fieseln23-Apr-22 5:43
Clark Fieseln23-Apr-22 5:43 
GeneralRe: My vote of 5 Pin
0x01AA23-Apr-22 6:16
mve0x01AA23-Apr-22 6:16 
GeneralRe: My vote of 5 Pin
Clark Fieseln23-Apr-22 6:32
Clark Fieseln23-Apr-22 6:32 
QuestionMy vote of 5 Pin
Steve Hageman16-Apr-22 6:04
Steve Hageman16-Apr-22 6:04 
AnswerRe: My vote of 5 Pin
Clark Fieseln16-Apr-22 7:48
Clark Fieseln16-Apr-22 7:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.