HN user

tejuis

32 karma
Posts0
Comments20
View on HN
No posts found.

I've used Emacs for 30 years. In the very beginning swapped capslock and control. After 20 years started to have minor problems with the pinky. A lot of keyboard use. Did not like evil mode for several reasons. It meant a lot of configuration and relearning commands. Tried out god-mode for a short while, but had some sharp edges, and went back to normal. Retried god-mode with more effort to make it work for me and never looked back. At that point it took a long time to get everything to muscle memory, though. Maybe two months to feel ok and years to feel good.

I use "i" to get into editing mode (normal emacs mode) and control-backspace to get to god-mode. I mostly use searching for moving in the buffers. In god-mode it means pressing "s" and then typing the search text with enter marking the search text completion. Then I use "." to search forwards and "," to search backwards. If I want to search for the word(s) at point, I do "s" and "enter" and then "j" to grow the search word, word by word. When ready, again "." searches forwards and "," backwards.

The most common operations, like changing buffers and emacs windows, are under single key press. Everything is quite effortless.

The best properties for god-mode for me is that you can use the same key combinations as normally, just producing them in a sligthly different way, and that you can also do keychord edits in editing mode if that is convenient. You don't have to exit editing mode at all, if that helps.

The obvious compromise with modal editing is that you have to know in which mode you are in and that some of the useful commands are not available in the editing mode, and then you have to switch modes to get them. However, with god-mode related editing mode you always have all the keys available, and you only have to know the mode you are in (at least partly). I would never settle with evil mode, but god-mode works very well. A lot of benefits with moderate amount of downsides, therefore a win all in all.

BTW that was a very interesting interview... I kind of agree. However, in order to automate something, you need to know the subject. For example, in digital circuit design, we use these EDA tools, but on top of that we create a lot of custom tools to automate all kinds of phases of the process. You definitely have to know the field you're working on.

My answer tried to be as generic as possible and leave out the "real world" and implementation details in general. There are fundamental limitations to what you can express in four sentences...

Compilation is "just" an intermediate step between code and using it in an interpreter. The interpreter can be pure hardware, mix of hardware and software, or you can do it on paper yourself (given time). These are implementation "details". Also, to me code is not a synomym with software, since I'm not referring to programmability, only to the transformation aspect. Programmability is an implementation detail. You may perform a transformation with hardware only, as mentioned above. The hardware itself could be described with code, e.g. in Verilog language. Manufacturing a silicon chip based on Verilog code is also an implementation detail.

In fact your CPU/PC is hardware and it is making transformation from input data to output data. In this case, besides the other input data, like keyboard input and files on disk, you may consider the binary code as part of the input data. This is where the confusion starts to happen. This is an implementation detail and should not be confused with the general notion of utility we want to have. Code is "only" the means to an end. We want to do stuff with the machine. We want to transform input data to output data, since that is essentially what we are after. For the goal of getting stuff done, data and code are separate things.

When saying "data is interpreted by code", it is partially correct. First of all data carries meaning, as in meaning for people (the data user). Data is encoded to some format (which is also data, but implementation data) and you could say that this encoding is "interpreted". However, the encoding is "just" an implementation detail which depends on the machine you are trying to use for the transformation. When the computer paints pixels on your monitor, it transfers data bits through display driver, through HDMI protocol (for example), to monitor itself. There are plenty of "interpreters" on that path. On the screen the pixels could represent the letter "A" and that could carry meaning for you (depending on the other stuff on the screen). This is why I stated that data is information about things. Encoded data is an implementation detail and varies between implementations, but the "true" data is essential and implementation independent. It carries meaning and utility value. Essential data is not interpreted by code. The user (a person) interpretes the essential data.

Code is data for the compiler. The compiler transforms source code (input data) to object code (output data). Code is data also for the interpreter. Algorithm contains all the essential information about the data transformations to be performed, and code in form of programming language text, is an implementation of that algorithm in the specific programming language. Algorithm is programming language independent. It's fair to say that by code I mean the algorithm. But also an algorithm must be presented in some form or another, so its code. :)

Data is information about things and code is information about how to transform data. Every program transforms input data to output data. Code is interpreted by the computer. Data and code are encoded to bits for the digital computer.

There are number of things you can do to improve your situation. Obviously you should not have packages loaded that you don't rely use. There are also ways to perform "lazy" loading, so that memory image is minimal until a package is really needed. I'm not using this myself.

My usage model is such that I only start one Emacs and use emacsclient to add files for editing from terminals. Emacs is running all the time (weeks/months).

Since Emacs is my primary interface to my Linux box, I give it some priviledges. In the .xsession I do:

vmtouch -t $HOME/.emacs.d; vmtouch -ld /usr/bin/emacs-gtk

Which effectively ensures that critical Emacs images and data is present in the memory all the time. Check out vmtouch utility. It might be useful for other purposes as well.

I've been running Emacs for almost 30 years. Stability has never been a problem and the last 5-10 years it has been rock solid for me. I can run Emacs for months without restart. Hence instability must be an issue with some packages.

There are some contradictions in here. If you map your functionality from software (on CPU) to FPGA, your software becomes hardware. FPGA is hardware. FPGA is retargetable, but it is not programmable in the sense what SW requires. The resources are preallocated, which is the essential property of hardware.

In software you allocate and deallocate memory, which is your dynamic resource. You don't have that in HW. You can implement a memory manager in HW, but your system will not be flexible enough to be soft. Also you can synthesize your CPU on FPGA, but the performance is poor compared to a hard CPU on FPGA.

Currently FPGAs are becoming popular in accelerating software functions to offload the CPU. The most powerful FPGAs have a lot of fixed logic CPUs in them (i.e. hard macros).

You can't just say that I'll just go ahead and run my software functions as hardware. For certain type of workloads you always need software, in practice (not in theory).

If your requirements are only high performance and improved type checking, then you are all set. The only language you need is C++. But the criteria above does not define all programming problems.

C++ is too complicated. "Nobody" writes C++, but a subset of it. Select your subset.

Dynamic typing in Lisp (and the like) is really nice for quick prototyping where program correctness is not key. You are exploring what you want to achieve with the program, be it algorithm level or architecture. C++ is not well suited for that, since type checking and memory worries are slowing you down, too many details to drag along. Hence no "silver bullet".

I agree. If you take the tool perspective, it is easy to understand that different tools are needed for different tasks. Don't use hammer for sawing. No single language scales to all corners of programming problems.

Actually I really don't get this endless "silver bullet" discussion. C has its place and for very good reasons. Also the author makes really good points about the integration aspects of C.

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. :)

It is easy to agree with these findings. In general it is important for humans to get answers to WHY. History answers typically to WHY questions and to some extent also examples, i.e. you see by example WHY the theory works.

Well, division was to be made with a binary counter. They typically work with rising edge clock, and you get a smaller circuit if you have 14 bits compared to 15 bits. I agree that 2^15 can be made to work by using both edges of clock signal.