Sorry for taking so long to reply, I was trying to find a good way to answer without creating a whole new series of videos or writing a book, but I failed and ended up writing a book and some new code for demonstration purposes.
And that still only ends up scratching the surface of the questions that you asked.
So how does it all look like?
At the moment, it’s a mess.
A lot of what I have done so far is hacked-together proofs-of-concepts.
But everything is still written with an eye to the future, so traces of my “secret sauce” that I am not yet ready to reveal to the world, are woven throughout every modules. (The building blocks are actually called modules in Verilog)
I was looking for parts that I could show you, but either they would be too trivial to really explain anything or too convoluted and spaghetti-like to clearly explain the underlying concepts.
So I took some time to write a simple, but representative oscillator module and heavily commented it to explain some of the syntactic quirks of Verilog and the underlying concepts.
And now, to answer some of your questions a little bit more precisely while staying unfortunately too abstract:
Everything in FPGA is described with HDLs, Hardware Description Languages, the two most used HDLs are Verilog and VHDL, the former is what I, and most of North America except the military, use, and the latter is what most of Europe uses. I’m not sure of the rest of the world’s preferences. But like EMACS over vi, Verilog (or specifically its descendant SystemVerilog) is clearly superior. 
So before you can describe an oscillator, you have to imagine it.
The goal of an oscillator is to produce a periodically varying value, and since we are in the digital domain, this happens in a discreet manner and a new value has to be produced regularly at a given sampling rate.
The oscillator has two parts, the first of which is a phase value that increments for every sample and loops around back to zero at the end of each period.
The easiest way to implement that in digital logic is with a counter, you are probably familiar with the 4-bits counters available in the 4000 or 74xx series of digital logic chips. They count up from 0 to 15 and back to zero.
So just put a clock on a counter and you have the first half of an oscillator, in the case of the 4-bit counter, it has a fixed frequency which is one sixteenth of the sampling frequency, no too useful, but a start.
The second part of an oscillator, is what forms the actual waveform. To each phase value corresponds a waveform value. In the case of a sawtooth ramping up, the value of the phase itself can serve as the waveform output, for a sine wave, one would typically have one cycle of a sine wave stored in a memory and use that as a lookup table (refinements could include storing only a quarter of the period to save memory and using the waveform’s symmetry to compute the other three quarters of the period, linear interpolation could also be used to limit the size of the lookup table).
With a 4-bit counter, the size of the lookup table would not be a problem, but in real life our phase counter would have to be significantly larger.
How large?
Let’s see, with a 48kHz sampling rate if we want to generate the lowest MIDI note at around 8 Hz (round numbers used for simplicity), our counter needs to be at least able to count to 6000 so at least 13 bits.
But how do we get the frequencies above 8Hz? The idea is to use an adder instead of a counter. That way for each sample the phase can increase by any given number at each sample. If we want to generate a note one octave above that 8Hz, we simply increment the phase by 2 every sample and it will take 3000 samples for our 13-bit phase to roll over and start a new period. 48kHz/3000 = 16Hz
Right away we notice that 13-bits doesn’t give us enough precision: increment by 1 and we get one note, increment by 2 and we get the note one octave higher. So how do we get the notes between those two? We simply increase the width of our phase, lets make it 20-bits wide instead of 13. To get the 8Hz note we now increment by 128 at every sample and to get the octave above that at 16Hz we increment by 256. We now have 128 values to chose from to get the 11 notes in between those two (it might still not be quite enough to get within a few cents of each note, but remember this is the lowest octave supported by MIDI, it is between 8Hz and 16Hz and is below the hearing range of most humans.
With 20 bits we have plenty of precision for the audible notes - already at the next octave we have 256 different values to get close to the 12 notes).
So what does this look like in SystemVerilog?
This is what it looks like in SystemVerilog:
(Sorry I don’t know ho to get Discourse to do syntax highlighting for Verilog.)
// osc.sv
// This module is the back end of the oscillator.
// It performs the phase incrementation and creates simple waveform that do not require lookup-tables.
// For flexibility, the value of the phase increment is computed elsewhere and provided as an input to this module.
// Output waveforms: sawtooth and square wave
// Outputs are unsigned values.
// For simplicity the output samples have the same width as the phase.
module osc
#(
// Modules can be parameterized for flexibility, and to enhance re-usability.
// For example if we wanted to turn this into an LFO we could just increase the PHASE_WIDTH parameter to achieve lower frequencies.
parameter PHASE_WIDTH = 20
)
(
// Here we declare the module's input and outputs.
// Inputs are typically just wires driven by other modules higher up in the hierarchy.
// Most digital logic is synchronous, meaning all changes happen on the rising edge of the system clock.
input wire clk,
// It is good practice to have a reset signal on all registers so we know what state the circuit starts in.
// The Altera FPGA hardware is optimized for asynchronous active low resets.
// (Circuit goes into reset when the rst_n pin goes low.)
// It is customary to indicate active low signals by appending _n
input wire rst_n,
// Buses are declared with this syntax [a:b] meaning that the bits will be numbered from b to a.
// It is customary to put the largest number (most significant bit) on the left.
// Most of the time, the bits in a bus that is N bits wide will be numbered from 0 to N-1
// We read [N-1:0] as "N-1 down to 0".
input wire [PHASE_WIDTH-1:0] increment_in, // This value is added to the phase accumulator at every clock cycle.
// It is good practice to have registers as outputs. It helps avoiding several potential timing issues.
// reg is the key word to declare registers.
output reg [PHASE_WIDTH-1:0] ramp_up_out, // The ramp waveform.
output reg [PHASE_WIDTH-1:0] square_out // The square waveform.
);
// Here we declare registers to store the state that need to be kept between each clock cycle.
// In this case we only have our phase register.
// The phase is an unsigned number incremented for each sample and saved.
reg [PHASE_WIDTH-1:0] phase;
// Here begins the main infinite loop
// This "always" block statement says that what is inside of it will happen at every rising edge of the clock ( @ posedge clk )
// or at any falling edge of the reset ( @ negedge rst_n ). (Asynchronous, active low reset.)
// It is good to have an asynchronous reset so that at power up, the circuit can be kept in reset until the clock stabilizes.
always_ff @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
// Active low reset asserted.
// Clear all our outputs and the phase counter during reset so we start from a known state.
phase <= 'd0; // The left arrow "<=" is the "non blocking" assignment which we use inside always blocks.
ramp_up_out <= 'd0; // 'd means a decimal value, 'h is for hexadecimal and 'b for binary, in hardware we often need to express precisely the value of every bit.
square_out <= 'd0; // You can also specify the width of the number in bits by adding a number before the apostrophe e.g. 3'b010 for a three bit wide number two
// When no size is specified but the apostrophe is present, the size is assumed to be the same as the left hand side.
// If the apostrophe and base is not present, the number is assumed to be a 32-bit decimal number.
// It is often necessary to exactly specify the size of numbers, and the compiler will issue warnings when it is not specified.
// But it can also be dangerous to use explicit size as, for example, if you first specify a number as 3'd7 but later realize it should be 8.
// If you just change it to 3'b8, you get 0 instead. It could take a while to find that bug because your brain will read 3'd8 as 8.
// You will get a compiler warning, but if you are not careful you will get tons of warnings (several thousands in large projects)
// so you may not notice the new warning.
// That is why I try hard to eliminate as many warning as possible and know the current count of "acceptable" or "unavoidable" warnings
// So I'll notice when there is a new one. But it isn't always easy to keep the warning count low.
// For example, to make sure this very simple file was free of syntax error, I threw it in Quartus and compiled it and got 12 warnings, 6 of which "critical".
// Don't worry, this file is clean, the warnings are just because other parts are missing to make a project out of this files.
// E.g. you have to define your clock speed, which pins of the chip are connected to the inputs and outputs,
// and funnily enough how many processor cores to use while compiling (5 of the 6 non-critical warnings are about this).
// Compilation of large projects can take several hours, you may not want to render your computer unusable (or have it melt) because the compiler is using all the cores at 100%.
end else begin
// We are now out of reset and operating normally.
// We do the following at every rising edge of clk.
// Add the increment to the phase
phase <= phase + increment_in; // This will automatically roll over to 0 when the count exceeds the size of the phase register.
// "Compute" the outputs waveforms from the phase
ramp_up_out <= phase; // It's just the phase as-is, really.
// Replicate the most significant bit for all bits so we get 0 for the first half of the period and 2**PHASE_WIDTH-1 for the second half.
// This syntax: {a {b} } means concatenate b, a times.
// phase[PHASE_WIDTH-1] is the most significant bit of phase.
square_out <= { PHASE_WIDTH { phase[PHASE_WIDTH-1] } };
end // else: !if( !rst_n )
end // always_ff @
endmodule : osc // Useful in large files with multiple modules, but normally you only put one module per file.
// Again I hope I didn't write all this for nothing and that someone read the whole thing.