Miscellaneous Tool/Command Line Suggestions
This was originally written as an Ed post intended for the students of Stanford’s Winter 2024 140E offering (which I TA’d).
I’ll periodically go through and update this more—you don’t need to read any/all of it, but hopefully it will contain something you find useful in working for this class. I unfortunately lost all of my progress when my laptop died., so I’ll be updating this version a little more this lab.
Navigating in the terminal
Keyboard shortcuts (note that these use emacs bindings by default, see the terminal
subheading under vim
if you’d prefer to use those):
-
CTRL+U
delete to the beginning of the line -
CTRL+K
delete to the end of the line -
META+B
go back a word (meta will be dependent on your terminal emulator settings, escape should always work, alt may or may not work) -
META+F
go forward a word (can be prefixed by aMETA+number
for a count) -
CTRL+R
to search for a particular command (can keep searching backward usingCTRL+R
or can go forward again usingCTRL+S)
-
CTRL+X CTRL+E
open$EDITOR
(maybe$VISUAL
) and execute the resulting commands from whatever file you save
Useful commands:
-
cd -
—change directory to the last directory you were in. -
pushd/popd
—used for keeping track of a stack of working directories (worth looking at other options, e.g., +1 which you can use to rotate the directory stack)
You can set up some aliases like Dawson talked about.
alias 140="cd $CS140E_2024_PATH/"
alias 140pi="cd $CS140E_2024_PATH/libpi"
alias 140l1="cd $CS140E_2024_PATH/labs/1-compile"
alias 140l2="cd $CS140E_2024_PATH/labs/2-trusting-trust"
alias 140l3="cd $CS140E_2024_PATH/labs/3-gpio"
alias 140l4="cd $CS140E_2024_PATH/labs/4-cross-checking"
alias 140l5="cd $CS140E_2024_PATH/labs/5-interrupts"
alias 140l6="cd $CS140E_2024_PATH/labs/6-threads"
alias 140l7="cd $CS140E_2024_PATH/labs/7-bootloader"
alias 140l8="cd $CS140E_2024_PATH/labs/8-uart"
alias 140l9="cd $CS140E_2024_PATH/labs/9-device-int"
alias 140l10="cd $CS140E_2024_PATH/labs/10-debug-hw"
alias 140l11="cd $CS140E_2024_PATH/labs/11-imu-i2c"
alias 140l12="cd $CS140E_2024_PATH/labs/12-i2c+mailboxes"
alias 140l13="cd $CS140E_2024_PATH/labs/13-ss-equiv"
alias 140l14="cd $CS140E_2024_PATH/labs/14-ss-equiv-deux"
alias 140l15="cd $CS140E_2024_PATH/labs/15-pinned-vm"
I like using fzf
to quickly navigate between directories. In my ~/.zshrc
, I have
alias cf.='cd "$(find . -type d | fzf)"'
alias cf='cd "$(find . -wholename '\''./*.*'\'' -prune -o -type d -print | fzf)"'
alias ve='vi "$(find . -type f | fzf)"'
cf
gives you a fuzzy finding prompt with directories below your current value, cf.
does the same thing but not ignoring hidden directories, and ve
lets you edit a file with a fuzzy finder (e.g., so you can do ve
and then type lab/1/1.c
to edit the part one code.
Useful Utilities
-
You can use
grep -r
to recursively search for a term. Other common flags I use include-l
(print the file name only), and-A
and-B
(for printing context). This can be useful when you want to know where something is called (e.g., where doessome_fn
appear->grep -r some_fn
orgrep some_fn *.S *.c
. I like usingripgrep
(rg
, https://github.com/BurntSushi/ripgrep) for this purpose, which is a multithreaded equivalent written in rust. -
bear
(https://github.com/rizsotto/Bear) is useful for use with theclangd
language server (if you’re usingvim/nvim
, I assume you know whether or not you’re using it—if you’re using vscode, I think it’s the default). If you just runbear -- make
, it will generatecompile_commands.json
such that your lsp can find definitions (works better than e.g.,ctags
since it can figure out functions made with theMK_FN
macro).
Vim/Neovim Config
I use vim/neovim, so I can’t give as much commentary to how to use vscode effectively. I use neovim preferentially over vim since I think the integration with lua as a scripting language leads to a richer plugin ecosystem, and I like that it has builtin lsp integration (although I miss my :term
using a pseudo terminal). Some plugins I find useful are
-
lazy.nvim
(https://github.com/folke/lazy.nvim) as my plugin manager—I think the plugin manager you pick is relatively arbitrary (in vim, I still use Vundle). It lazily loads plugins which can be nice. -
VonHeikemen/lsp-zero.nvim
—nice utility for handling lsp things so I just kind of do’t think about them, especially withwilliamboman/mason.nvim
andwilliamboman/mason-lspconfig.nvim
which make it so I can:LspInstall
when I open a new file type where I haven’t downloaded the lsp yet.
Separately, I like using junnegunn/fzf
and junnegun/fzf.vim
which make it convenient to switch between files/buffers/etc… I have the following lines
nnoremap <c-f>f :Files
nnoremap <c-f>b :Buffers
nnoremap <c-f>L :Lines
nnoremap <c-f>l :BLines
nnoremap <c-f>r :Rg
nnoremap <c-f>w :Windows
nnoremap <c-f>t :Tags
nnoremap <c-f>m :Marks
nnoremap <c-f><c-f> :Files<cr>
nnoremap <c-f><c-b> :Buffers<cr>
nnoremap <c-f><c-L> :Lines<cr>
nnoremap <c-f><c-l> :BLines<cr>
nnoremap <c-f><c-r> :Rg<cr>
nnoremap <c-f><c-w> :Windows<cr>
nnoremap <c-f><c-t> :Tags<cr>
nnoremap <c-f><c-m> :Marks<cr>
Splits
I like using splits and having my terminal in my vim instance; the relevant commands and keystrokes include :term
(open a terminal), :split
, :vsplit
, and <C-W>
prefixed commands (<c-w>n
split the current buffer horizontally, <c-w>v
split the current buffer vertically, <c-w>[hjkl]
move to the pane to the direction specified in the normal vim way, <c-w><c-w>
alternate between buffers, <c-w>[HJKL]
move the current pane in the direction specified by the letter, <c-w>r
rotate the list of panes, <c-w>_
maximize the current pane vertically, <c-w>|
maximize the current pane horizontally, <c-w>+
increase height (takes an optional count), <c-w>-
decrease height, <c-w><
decrease width, <c-w>>
increase width, <c-w>=
equalize splits). I’ll sometimes use tabs as well, normally using :tabe
and gt
, gT
to navigate between them.
Terminal
As mentioned under the main terminal navigation subheading, while the default terminal emulator settings probably use emacs key bindings, you can easily switch this to using vim keybindings. In bash
or zsh
the 1-second fix is to run set -o vi
(you might want to throw that in your .bashrc
or .zshrc)
. In bash
, the equivalent to C-X C-E
is <ESC>v
, in zsh
, it’s a little more complicated (https://unix.stackexchange.com/questions/6620/how-to-edit-command-line-in-full-screen-editor-in-zsh). In bash and other programs that use gnu readline
(python
is the other obvious one off the top of my head, although also gdb
, sadly not node
), you can modify your .inputrc
to achieve a similar effect just throw in the lines
set editing-mode vi
set keymap vi-command
140E Stuff
debugging asm
You can just b
to a function you’ve defined in C, where you’re likely more comfortable (I’ll normally just define a function like void tmp_debug(uint32_t reg1)
that calls clean_reboot
at the end (since normally we clobber registers in a way that makes the rest hard to interpret). If you get a linker error since you’re branching to something that doesn’t exist in a given test case, you can either change your Makefile
so you’re only looking at the failing test case, or you can add a .weak
symbol in the asm file with the name you’re using.
When to read-modify-write
The basic idea is we read modify write when we want to keep some of the old state. There are some heuristics you can use for this: “can I give sensible values to every bit/byte?” (yes->probably write whole thing, no->probably rmw).