It seems that it has been done, because it is possible for the scope that exists so far. However, there are a lot of obstacles on the way forward...

I have a lot of experience in writing Verilog for ASICs and I also have significant experience in software design. On top of this I have played with similar ideas in past and implemented all kinds of stuff related to this.

The bottom line is that it is very difficult to get this to scale. When you develop RTL, you have to be able to simulate it. There is no simulator (yet).

Using Racket as code generator is OK, but then there should be proper error checking for RTL semantics. This would be easier to achieve with just parsing any LISP style RTL (invent your own syntax) and generate Verilog out of that. That kind of parsing and checking can be done in Racket or C (or whatever language you prefer). Verilog95 is quite redundant in syntax, but the later Verilog versions and SystemVerilog are very sensible. There is actually very little you can gain by adding one layer of complexity on top of Verilog. If we assume that you could somehow rise the abstraction level significantly from Verilog, then the problem would become: how to annotate Verilog errors back to the "high" level Racket. The semantic gap is now too large. You loose controllability.

It is possible to do the simulator with Racket as well. The performance of such a simulator will be poor (yes, I have done simulators as well). Since the late 1990s RTL simulation has be done with "native compilation". The original implementation was interpreting, which is good when fast simulator start is preferred. However, when you run long simulations, you really need to break RTL into peaces and map everything straight to CPU instruction set. If you wanted to have fast RTL simulation, you would have to be able to explicitly control certain aspects. First of all you should be able to manipulate low level data effectively. It's reasonable in C with bit shifting and masking. Not so much in Racket, although it uses JIT, so it's probably not hopeless. HW is fixed in nature. All resources in your RTL model are going to exist for the duration of the whole simulation. In C you can allocate everything in nice order and you will gain good CPU cache behavior in your simulation. In Racket you have GC (garbage collection) and random placement of data in memory. The cache utilization will be poor, and the simulation would be 100x slower. Even if you keep references to all data in Racket all the time, the GC has to check for whether this data should be garbage collected or not. Most data is HW and it is static, so this is all redundant. Also there would be so much more data in Racket than in C implementation.

Then there is the issue of concurrency. HW is parallel and SW is serial. SW must model the parallel nature in simulation. There are maybe 10000M transistors in modern CPUs. That translates (very very roughly) to 100M flip-flops. In SW terms you have 100M (minimum, in practice more) threads. Not possible as OS threads, so you can do Green Threads to cover that number, but performance is useless. In the end you have to do it "manually" by using functions and modeling concurrency on data level. For each flip-flop you maintain new-input-value and update current-output-value when HW clock ticks. This is the basic method for RTL level simulation. Netlist simulation is much more detailed and hence much slower. RTL simulation has some more complications, but it's not that far from what is said above.

Ok, hobby projects does not have 10000M transistors, but they have to be debugged. You can use "printf" debugging with HW as well, but at some point you need waves. Waves display bit level values on a timeline. Time is essential since HW is parallel. You can get to waves with Racket, but you have to instrument each data point that you want visible with value dumping features. Again, it is possible with metaprogramming, but it really makes your head hurt and simulation slooooooowww.

With all that said, best of luck going forward. :)