Refer to "record" and "play" log entries for the descriptions of the design and implementation.
Now your FPGA-based retro computer can have its own mass storage too - just like it did circa 1980!
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
Refer to "record" and "play" log entries for the descriptions of the design and implementation.
sys_cas_mercury.bitBinary to be uploaded to Mercury + Baseboard Switches to be set (7 downto 0): 00111100 (600bps, 8bits, no parity, 2 stop bits, show bps on 7seg LEDs)bit - 146.11 kB - 11/11/2020 at 07:15 |
|
(refer to https://github.com/zpekic/Sys_cas/blob/main/Mercury/tapeuart.vhd )
Remember, recording was done using simple "binary frequency shift keying" with following parameters:
bps | mark ("1") frequency (Hz) | space ("0") frequency (Hz) | fmark ---- bps | fspace ---- bps |
300 | 4800 | 1200 | 8 | 4 |
600 | 9600 | 4800 | 8 | 4 |
1200 | 19200 | 9600 | 8 | 4 |
Result is a distorted waveform on the tape, but which still holds the primary mark and space frequencies. The task is to figure out those frequencies in real time, and based on which one is detected, output 0 or 1 which will be the UART output (TXD).
This is of course a classic FFT or Goertzel algorithm problem, but neither of those is used here. The brute force of FPGA and high bandwidth of modern A/D adapters is used instead, with some simple hacky tricks. Here are the steps:
1. Connect the available A/D converter and drive it to sample tape data
The Mercury baseboard uses the A/D converter described here. I used the driver code provided as a sample. The clock is 25MHz, and the sampling frequency 0.75MHz - both are clearly overkill for the purpose, but they are easily obtainable by simple clock dividers. The channel is either of the audio left or right.
-- Mercury ADC component
ADC : entity work.MercuryADC
port map(
clock => adc_clk,
trigger => adc_trigger,
diffn => '0',
channel => "000", -- channel 0 = left audio
Dout => adc_dout,
OutVal => adc_done,
adc_miso => ADC_MISO,
adc_mosi => ADC_MOSI,
adc_cs => ADC_CSN,
adc_clk => ADC_SCK
);
2. Process each A/D conversion
adc_done signal will pulse when the conversion is ready, at which point the data reading (strength of the signal from tape in 10-bit resolution should be analyzed) is presented at Dout port. We don't really care about the value, but the point where the value is very close to zero, hoping that moment indicates the period of the waveform that captures the main recorded mark/space frequency.
Picking this threshold value can be tricky and depends on the characteristics of the signal path between audio input and A/D converter. In this case hex value 0x12 is selected, but to make it easier to figure it out, the min and max value (wave amplitude) is captured and can be displayed through debug port.
When it is detected that previous value was close to zero but is now greater then 0x12 then 1 output is generated, otherwise 0. Note that there is a certain level of hysteresis in this as the previous value of f_in_audio is taken into account.
-- ADC sampling process
on_adc_done : process (adc_done, f_in_audio)
begin
if (rising_edge(adc_done)) then
if (f_in_audio = '0') then
if (unsigned(adc_dout) > "00" & X"12") then -- 24
f_in_audio <= '1';
end if;
else
if (unsigned(adc_dout) < "00" & X"12") then -- 24
f_in_audio <= '0';
end if;
end if;
if (unsigned(adc_dout) > max) then
max <= unsigned(adc_dout);
end if;
if (unsigned(adc_dout) < min) then...
Read more »
(refer to https://github.com/zpekic/Sys_cas/blob/main/Mercury/tapeuart.vhd )
The recording is simple "binary frequency shift keying" with following parameters:
bps | mark ("1") frequency (Hz) | space ("0") frequency (Hz) | fmark ---- bps | fspace ---- bps |
300 | 4800 | 1200 | 8 | 4 |
600 | 9600 | 4800 | 8 | 4 |
1200 | 19200 | 9600 | 8 | 4 |
All of this is accomplished with simple multiplexer that transfers one of two incoming frequencies above to audio_left and audio_right pins (no stereo signal, but mono replicated on both channels if tape recorder uses only one):
-- output path
f_out <= freq_space when (serin = '0') else freq_mark; -- always output to audio
audio_left <= f_out;
audio_right <= f_out;
Given that frequencies are square waves which have infinite number of harmonics (sine waves), first of which is of the base frequency, and others rapidly attenuating odd multiples, and that these cannot be all stored on the tape, the signal that is actually stored is a distorted square/sine wave (see screenshot from oscilloscope). The D/A conversion happens on the path from FPGA output pin towards the tape. For other FPGA, a real D/A converter could be used which could easily generate very close approximation of a smooth sine wave of given mark/space frequency. But in the end it doesn't matter, as long as the receiver side can pick up the main harmonic component somehow from the analog signal recorded on the tape.
It is important to note that regardless of baudrate, each "1" will always result in 8 cycles and each "0" of 4 cycles within the bit time. This big 2/1 difference allows differentiation between 1 and 0 on the receiving side, but presents a problem - mark frequency of 19.2KHz is close to the upper reliable frequency reproduction limit of a cassette tape.
Create an account to leave a comment. Already have an account? Log In.
Yes I do! Good point I will add that to the list of components :-)
Become a member to follow this project and never miss any updates
What's a "cassette"? 😉
Cool! 👍 Do you stll have a hexagonal pencil to rewind the tape?