Checking my mouse wheel on Linux

I had some problem with my mouse wheel (on Microsoft Wireless Mobile mouse 3500, by the way): The scrolling would sometimes be jerky. I was turning the wheel to scroll down, and there were small jumps upwards. How annoying is that?

But how could I know if it’s the mouse’s fault, or maybe a software issue? Look at the raw mice events, of course (as root).

# hexdump -v -e '3/1 "%02x " "\n"' /dev/input/mice

But this is no good. The data that represents the mouse movements appears to be useful, but turning the mouse wheel it says

08 00 00
08 00 00
08 00 00
08 00 00

regardless of whether it’s rolled up or down. That’s because the three-byte PS/2 protocol doesn’t support a mouse wheel. Hmmm…

So the EVDEV interface has to be used.

Program follows, suppose it’s saved as event.c. Note that it filters out anything but mouse wheel events.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <linux/input.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
  int fd;
  struct input_event event_data;

  if((fd = open(argv[1], O_RDONLY)) == -1) {
    perror("opening device");
    exit(EXIT_FAILURE);
  }

  while (read(fd, &event_data, sizeof(event_data))) {
    if ((event_data.code == 8) && (event_data.type == 2) &&
	((event_data.value == 1) || (event_data.value == -1)))
      printf("time %ld.%06ld: Value=%d\n",
	     event_data.time.tv_sec, event_data.time.tv_usec,
	     event_data.value);
  }
  return 0;
}

Compile it with (say)

$ gcc -o event -O3 -Wall event.c

And then, as root, first find which of the event files is related to the mouse by using hexdump and moving the mouse. When choosing the right one, a lot of text appears. In my case, it was

# hexdump -C /dev/input/event6

So now run with the right argument:

./event /dev/input/event6

and in my case I rolled the wheel downwards getting

time 1400332306.969728: Value=-1
time 1400332306.985722: Value=-1
time 1400332307.001774: Value=-1
time 1400332307.018793: Value=-1
time 1400332307.057738: Value=1
time 1400332307.465760: Value=-1
time 1400332307.506717: Value=-1
time 1400332307.521738: Value=-1
time 1400332307.545747: Value=-1
time 1400332307.570738: Value=-1

Aha! Time to buy a new mouse!

Vivado: An ISE guy’s exploration notes

Just a few things I wrote down for myself, as I got acquainted with Vivado 2014.1 (having worked with ISE until now).

Kicking off in Linux

$ cd trashcan-directory
$ . /path/to/Vivado_2014.1/Vivado/2014.1/settings64.sh
$ vivado &

or running Vivado with a certain Tcl script

$ vivado -mode batch -source /path/to/project.tcl

or implementing a project without GUI:

$ vivado -mode tcl

and then at Vivado’s Tcl prompt:

open_project /path/to/example.xpr
launch_runs -jobs 8 impl_1 -to_step write_bitstream
wait_on_run impl_1
exit

Of course, this can be the content of a file, in which case the -batch is used to run it (the “exit” at the end is then redundant).

In batch mode, Tcl code is printed out as it’s executed. The -notrace flag on vivado’s command line shuts this off. This flag also applies to the “source” command inside a Tcl script.

Jots

  • Vivado is very sensitive to which directory it’s run from. Not just journal files, but also Tcl script executions. New projects are created in that directory by default.
  • One can’t just move a Vivado project. Many file dependencies are internally stated as absolute paths (IP in particular)
  • “reset_project” in Vivado’s Tcl console is probably the parallel to “Cleanup Project files”. Not really, but close.
  • The instantiation template of a Block Design (e.g. “system”) is given as e.g. hdl/system_wrapper.v in the block design’s dedicated directory.
  • Make sure that the flow navigator is at the left. Or restore it with “View > Show Flow Navigator”. Otherwise it gets confusing.
  • Vivado has no problem importing NGC files (synthesized netlists from ISE) and EDIFs. As a matter of fact, NGC files are translated into EDIFs by Vivado automatically as the Vivado project is implemented.
  • DCP = Design checkpoint. It’s a file blob resulting from the partial implementation of a the project or its sub-blocks (e.g. IPs). It’s a plain ZIP file, so you can change its suffix to .zip and open it as usual. Some of the information inside is readable, e.g. an EDIF representation of the design (in post-synthesis DCPs), XDC files containing the constraints as interpreted by Vivado, stub files etc.
  • For the optimistic: To import some settings from an XPS .xml file: Right-click the Zynq block, pick Customize block, and pick Import XPS Settings (at the top), and pick .xml file. Use the file in system/data, because the exported file is empty(!). Only some attributes are imported. This doesn’t work very well.
  • To import an IP from another path (packed as IP), go to IP Catalog, right-click > IP Settings… > Add Repository
  • In an IP, all information is stored in component.xml. Interfaces, ports, file names. Everything. More about it in this post.
  • But the file to be included in a project for defining the IP is the XCI file. The component.xml file is generated as Vivado “unpacks” the project.
  • Vivado’s IP core definitions are at /path/to/Vivado_2014.1/Vivado/2014.1/data/ip/xilinx/
  • When packaging an existing core, use “package your project” and not “Create a new AXI4 peripheral”, even if a new AXI peripheral is created. The latter creates an empty core.
  • Definitions of bus interfaces (“conduits”) can be found in /path/to/Vivado_2014.1/Vivado/2014.1/data/ip/interfaces/. According to this thread and this one, there is probably no sane way to make custom interfaces.
  • Unlike ISE, don’t include a black box HDL file in the sources when an IP is used. Just add the IP’s XCI file. There should be no system.v, no fifo32x512.v etc.
  • In XCI/XML files, $PPRDIR can be used to represent the project’s root directory (where the .xpr is), possibly in conjunction with ../../-kind of paths for directories above it. This is true for paths given without quotes as well (e.g. USER_REPO_PATHS in XCI files).
  • In a block design involving a Zynq processor, protocol converters are found in the .bd file as “axi_protocol_converter” and with names going e.g. “system_auto_pc_19″. In general, “pc” and “auto_pc” are related to protocol conversion.
  • Unlike ISE, there is no such thing as “unrelated clocks” in Vivado. If two clocks are from origins with unknown relations, they are considered “Timed” and “unsafe” in the Clock Interaction report. The relevant paths will be calculated, and are likely to fail timing. To handle this, either make a false path between each pair of such clocks, or use set_clock_groups to tell set up groups of clock that are mutually unrelated.
  • There was a problem with connecting KC705 to Vivado 2015.2 over JTAG. Xilinx’ JTAG cable worked fine, and KCU105 too. Comparing with my laptop computer, which worked fine with the same Vivado version, it turned out that when things work properly, /dev/ttyUSB0 appears briefly and is soon removed, and only /dev/ttyUSB1 is left when the KC705 card is connected. On the computer it didn’t work on, both /dev/ttyUSB0 and /dev/ttyUSB1 were left. The dmesg log showed that the proper sequence is that the first USB interface is “disconnected” almost immediately after connection. This sounds very much like a Cypress EasyUSB re-enumeration that didn’t complete properly on the faulty computer. My hunch is that’s it’s a firmware loading issue, but I didn’t follow this up. On both computers the permissions were the same (rw for root user and group, the latter was dialout). It’s not the issue.

Some Tcl insights

There’s a post about setting up a Vivado project from scratch using Tcl. And another post on XDC constraints.

  • File > Write Project tcl… is very useful for getting the project in a nutshell. There’s also File > Export… > Export Block Design, which creates a Tcl file that sets up the block design (command write_bd_tcl).
  • The real Tcl documentation is with “help” commands at Tcl prompt, e.g. “help startgroup”. Or UG835.
  • Run a Tcl script:
    source {/path/to/script.tcl}
  • To test “get_ports” and such, open “Elaborated Design” (on the Flow navigator) or use “open_run synth_1 -name netlist_1″. get_ports and get_pins etc. work on that set. Or all properties with
    set x [get_port "DDR_WEB"]; foreach p [list_property $x] { puts "$p = [ get_property $p $x ]" }
  • The Tcl script for a certain run is given as e.g. project.runs/impl_1/ptoject.tcl. There’s also a runme.sh file in the same directory, which can run standalone for that run.
  • Using launch_runs in batch mode will cause the shell prompt to be returned before the implementation is done. Use wait_on_run to block until the run is done.
  • Obtain a timing report on paths (50 worst) going through a certain set of nets:
    report_timing -name my_timing_report -nworst 50 -through [get_nets -hier -filter {name=~*/wr_client_0_ins/*}]

    or from a set of cells:

    report_timing -name my_timing_report -nworst 50 -from [get_cells -hier -filter {name=~*/wr_client_0_ins/*}]

    Note that the -name parameter causes the output to go to a timing tab in the GUI. Without it, a textual report goes to the console. Or if -file is specified, to a file.
    And report all I/O ports’ timing:

    report_timing -delay_type min_max -max_paths 100 -name timing_1 -to [get_ports -filter {direction==out}]
    report_timing -delay_type min_max -max_paths 100 -name timing_2 -from [get_ports -filter {direction==in}]
  • Search for nets matching a name patters (where “*” may substitute slashes):
    join [ get_nets -match_style ucf */sys_clk* ] "\n"
  • Get the cells touching a net (including the hierarchy) or the BELs:
    join [get_cells -of_objects [ get_nets -match_style ucf */pcie_ref_clk* ] ] "\n"
    join [get_bels -of_objects [ get_nets -match_style ucf */pcie_ref_clk* ] ] "\n"

The PS pins bug

The ARM processor in a Zynq architecture (“PS”) has many pins that are connected directly to the processor’s hard IP core, without any relation to the FPGA (“PL”) logic fabric. The obvious evidence to this fact is that the processor is able to boot before the PL part is configured. The attributes of these pins are controlled completely by the ARM processor’s registers, and regardless of the PL configuration, if any.

And still, Xilinx has chosen to include these pins in the processor’s model in the logic design. In sample HDL designs, these pins are wired from the processor’s instance to the toplevel ports, as if it mattered. This wiring is completely meaningless to the bitstream that is compiled from the HDL. In fact, it’s probably meaningless in any respect.

As for Vivado, it seems like four signals, PS_CLK, PS_PORB, PS_SRSTB, and DDR_WEB (plus sometimes DDR_CLK) are treated, at some stages, as general I/Os and not direct connections to the PS7. This is evident in the toplevel schematic view of the project, where IBUFs an OBUFs are added to the PS_ ports, and DDR_CLK’s port is left floating. This causes Vivado to allocate “real” PL I/O pins to these signals, and then complain about them being unplacable and/or unroutable. The simple solution to this is to disconnect these signals from the toplevel port, possibly by turning them to wires, rather than ports, on the toplevel module. Vivado responds with treating these as useless signals, and therefore doesn’t bother itself any more with them. Except for complaining that the placement constraints in the processor-dedicated XDC constraint file can’t be applied.

In fact, there is no reason to connect any of these PS signals to real ports in Vivado. Having said that, ISE related tools may complain if these signals aren’t connected to ports, in particular when the toplevel module is in VHDL.

So the name of the game is to connect those PS signals to the toplevel port, or disconnect them, whatever makes the tools happy. It doesn’t make any real difference anyhow.

Getting the frequency of a clock

I wanted to be sure that the DDR memory controller’s user interface frequency was what I thought it was. That turned out no to be so simple. I implemented the example design, and tried to find what stands behind c0_clk or aclk or c0_ui_clk or any of the names it has. But in Vivado, nets tend to have many names (“aliases”), depending on their names in different modules. Someone must have thought it’s a great idea.

True, it’s possible to get a list of clocks in the timing report, or use the Tcl command

report_clocks

but there was no sign of anything resembling the known names mentioned above. Now, there must be a way better than what I did, but this is what I can suggest: Pick a register that is clocked by the desired clock. For example, in mig_7series_v2_0_tg.v, it says

  always @(posedge clk) begin
    if (tg_state[TG_IDLE] | tg_state[TG_UPDT_CNTR])
      curr_rd_ptr <= 1'b0;
    else if (cmd_rd_en)
      curr_rd_ptr <= ~curr_rd_ptr;
  end

and clk is known to be the AXI clock.

Now look it up. Method #1 is to find the clocks attached to a cell: In the Tcl console

get_clocks -of_objects [get_cells -hierarchical -filter {name=~*cmd_rd_en*}]

Assuming that only one clock is given, look it up in the list of clocks of the Timing Summary Report.

To be absolutely sure, let’s go to method #2: Produce a mini timing report for the cell. Again, in the Tcl console (the get_cell query is equivalent, given here for variety):

join [ get_cells -match_style ucf *cmd_rd_en* ] "/n"
c0_u_axi4_tg_inst/traffic_gen_inst/cmd_rd_en_i_1/nc0_u_axi4_tg_inst/traffic_gen_inst/cmd_rd_en_reg/nc1_u_axi4_tg_inst/traffic_gen_inst/cmd_rd_en_i_1__0/nc1_u_axi4_tg_inst/traffic_gen_inst/cmd_rd_en_reg

OK nice, there’s one single register with matching that name. It’s a bit hard to be sure we’re exactly on the spot, but that is soon resolved.

To get the clock information, get specific timing info for that specific cell:

report_timing -from [ get_cells -match_style ucf *cmd_rd_en* ]

[ ... ]

Timing Report

Slack (MET) :             5.327ns  (required time - arrival time)
  Source:                 c1_u_axi4_tg_inst/traffic_gen_inst/cmd_rd_en_reg/C
                            (rising edge-triggered cell FDRE clocked by clk_pll_i_1  {rise@0.000ns fall@3.000ns period=6.000ns})
  Destination:            c1_u_axi4_tg_inst/traffic_gen_inst/wr_proc_reg/D
                            (rising edge-triggered cell FDRE clocked by clk_pll_i_1  {rise@0.000ns fall@3.000ns period=6.000ns})
  Path Group:             clk_pll_i_1
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            6.000ns  (clk_pll_i_1 rise@6.000ns - clk_pll_i_1 rise@0.000ns)
  Data Path Delay:        0.633ns  (logic 0.266ns (42.005%)  route 0.367ns (57.995%))
  Logic Levels:           1  (LUT4=1)

[ ... ]

The full output also showed the worst timing path, but that’s not the point.

What we have here is the source and destination points in human-readable format, so there’s no confusion about which register we’re talking about. And there’s the definition of the clock, and its period, 6 ns (scroll to the far right). So there we have it. The AXI clock, in this case, runs at 166.66 MHz.

Getting a list of paths between clocks

Knowing which clock is which, this information can be used to list paths that cross clock domains. This is useful to verify that no mixed-clock path has sneaked in inadvertently. For example,

join [get_timing_paths -filter {name=~*/rd_client_7_ins/*} -max_paths 100000 -from [get_clocks clk_pll_i] -to [get_clocks clkout2]] "\n"
WARNING: [Timing 38-164] This design has multiple clocks. Inter clock paths are considered valid unless explicitly excluded by timing constraints such as set_clock_groups or set_false_path.
{mem_top_ins/rd_client_7_ins/localreset_reg/C --> mem_top_ins/rd_client_7_ins/localreset_sync_reg/D}
{mem_top_ins/rd_client_7_ins/localreset_reg/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/rstblk/ngwrdrst.grst.g7serrst.rd_rst_asreg_reg/PRE}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[2]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[2]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[4]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[4]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[6]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[6]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[7]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[7]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[1]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[1]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[0]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[0]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[3]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[3]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[5]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[5]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[8]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[8]/D}
join [get_timing_paths -filter {name=~*/rd_client_7_ins/*} -max_paths 100000 -to [get_clocks clk_pll_i] -from [get_clocks clkout2]] "\n"
WARNING: [Timing 38-164] This design has multiple clocks. Inter clock paths are considered valid unless explicitly excluded by timing constraints such as set_clock_groups or set_false_path.
{mem_top_ins/rd_client_7_ins/consumed_toggle_reg/C --> mem_top_ins/rd_client_7_ins/consumed_toggle_sync_reg[0]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[8]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[8]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[5]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[5]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[6]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[6]/D}
{mem_top_ins/reposition_7_ins/allow_new_frame_reg/C --> mem_top_ins/rd_client_7_ins/allow_new_frame_d_reg/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[4]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[4]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[1]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[1]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[3]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[3]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[7]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[7]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[0]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[0]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[2]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[2]/D}

Important: It seems like -max_paths counts paths prior to filtering (on Vivado 2014.1), which is why several paths shown above didn’t appear when it was set to 20. So to be sure to cover all paths, eventually give a very large number, as shown above.

Note that there’s an implicit -hier flag enabled, so * matches across hierarchies. As seen in the example above, it’s enough that one of the endpoints match the string. The paths shown in the example above are ignored for timing, so this proves that all paths should appear, even those not timed.

Programming the FPGA on Linux

The interface in Vivado with hardware cables is through two servers: vcse_serv which listens to TCP port 60001 and hw_server listening to TCP port 3121. These are usually started automatically when Vivado is launched, but they can be kicked off manually with the hw_server command, in particular when the server needs to be accessed from a remote computer.

A common problem is that the server runs with the privileges of the user who started Vivado, but accessing the device files that are related to the configuration cable requires root access. Vivado will say that “There is no valid target connected to the server”. In other words, no card was detected — because the server wasn’t granted access.

Recent development boards arrive with a USB plug for direct connection to the PC, so a PC cable isn’t required. For the KC705 board, the USB support origins from Digilent.

There is an explanation for how to install the drivers on this page. It tells me to change directory to {some path}/cable_drivers/lin64/digilent/ and run ./install_digilent.sh as root. I don’t run stranger’s scripts as root. But in this case it isn’t too bad. It just says

cp -f 52-xilinx-digilent-usb.rules /etc/udev/rules.d/52-xilinx-digilent-usb.rules
chmod 644 /etc/udev/rules.d/52-xilinx-digilent-usb.rules

Looking at the udev file in question, it merely allows read-write access to everyone (mode 0666) for device files generated by devices with vendor ID 0x1443 or vendor ID 0x0403 and the Manufacturer string set to “Digilent”. Seems harmless enough to me.

After installing the udev rule, disconnect and reconnect the cable, and kill the two server processes. After this, Vivado detects the card OK. In fact, going

$ killall hw_server

seems to be necessary every now and then, when the card has gone off and on again, and isn’t detected.

Or maybe… I should have run the all-driver installation script to begin with? First fxload and libusb (is it necessary?)

# apt-get install fxload libusb-dev

Under Vivado/2014.1/data/xicom/cable_drivers/lin64/install_script/install_drivers/, as root:

# ./install_drivers
--Install log = /.xinstall/install.log
--Installing cable drivers.
--Script name = ./install_drivers
--HostName = .....
--Current working dir = ...
--Kernel version = 3.13.0-35-generic.
--Arch = x86_64.
--Installer version = 1101
--Unsetting ARCH environment variable.
--User has root permission.
--Installing USB drivers------------------------------------------
--File /etc/hotplug/usb/xusbdfwu.fw/xusbdfwu.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusbdfwu.hex = 0000.
--Updating xusbdfwu.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xlp.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xlp.hex = 0000.
--Updating xusb_xlp.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_emb.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_emb.hex = 0000.
--Updating xusb_emb.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xpr.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xpr.hex = 0000.
--Updating xusb_xpr.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xup.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xup.hex = 0000.
--Updating xusb_xup.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xp2.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xp2.hex = 0000.
--Updating xusb_xp2.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xse.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xse.hex = 0000.
--Updating xusb_xse.hex file.
cat: /etc/hotplug/usb.usermap: No such file or directory
--Adding Product ID 0007 to the usermap.
--Adding Product ID 0009 to the usermap.
--Adding Product ID 000d to the usermap.
--Adding Product ID 000f to the usermap.
--Adding Product ID 0013 to the usermap.
--Adding Product ID 0015 to the usermap.
--Adding Product ID 0008 to the usermap.

--Digilent Return code = 0
--Xilinx Return code = 0
--Return code = 0
--Driver installation successful.

But that’s based upon /etc/hotplug, which Ubuntu doesn’t seem to care much about. But reading through /etc/hotplug/usb/xusbdfwu it’s quite evident how to match between the firmware files and the device IDs, leading to the conclusion that my red JTAG platform cable should be programmed with the firmware with

# fxload -v -t fx2 -D /dev/bus/usb/002/010 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xp2.hex

The bus path 002/010 was derived from its appearance in lsusb:

Bus 002 Device 010: ID 03fd:0013 Xilinx, Inc.

The interesting thing is that the device’s LED goes on only following the load of the firmware.

So let’s make a udev file of this, based upon this post: Copy-paste this into 52-xilinx-jtag-platform.rules

# udev rules for programming Xilinx' JTAG platform cables
ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0008", MODE="666"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0007", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusbdfwu.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0009", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xup.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="000d", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_emb.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="000f", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xlp.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0013", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xp2.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0015", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xse.hex -D $tempnode"

The platform cable’s LED should go on soon after plugging it into the computer. This should work on any platform cable.

If this doesn’t work, make sure manually that the programming with fxload works.

Programming the QSPI flash

Starting with Vivado 2014.1, the mcs file can be created with Vivado. Before this version, ISE’s promgen had to be used. Let’s assume a QPSI flash of 128M (e.g. N25Q128) connected to a Virtex device.

The command for creating an MCS file is

write_cfgmem -format mcs -interface SPIx4 -size 128 -loadbit {up 0x0 /path/to/virtex.bit} starter-virtex.mcs
Creating config memory files...
Creating bitstream load up from address 0x00000000
Loading bitfile /path/to/virtex.bit
Writing file /path/to/starter-virtex.mcs

As mentioned in the second post of this thread, the following lines should be added to a constraint file (.xdc) before implementing the design:

set_property BITSTREAM.Config.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]

The second line sets the configuration rate to 50 MHz, which is probably desirable for a quick load. The first line is required for the MCS file creating. Failing to have it will lead to the following error when attempting to build the mcs file:

write_cfgmem -format mcs -interface SPIx4 -size 128 -loadbit {up 0x0 /path/to/virtex.bit} starter-virtex.mcs
Creating config memory files...
Creating bitstream load up from address 0x00000000
Loading bitfile /path/to/virtex.bit
ERROR: [Vivado 12-3619] Cannot create SPIX4 PROM for bitfile /path/to/virtex.bit with SPI_buswidth setting of "None".
ERROR: [Common 17-39] 'write_cfgmem' failed due to earlier errors.

For those who forgot to do this, and don’t feel like re-implementing the entire design, open the implemented design, and create the bitfile with the property set.

set_property BITSTREAM.Config.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
write_bitstream /path/to/virtex.bit

Note that using the GUI button for this purpose will not work.

It’s also possible to set the SPI_BUSWIDTH and CONFIGRATE properties by opening Tools > Edit Device Properties… while having an implemented design opened (otherwise this menu items doesn’t appear).

Open the hardware manager, and open the target, just like for programming a bitfile. Then pick “Add Configuration Memory Device” at the bottom of the Flow Navigator to the left, and choose the FPGA for which the flash should be programmed. The next popup window suggests to program the device immediately. Go for it. In the following window pick the MCS file generated above, and pick the “Erase”, “Write” and “Verify” options (“Blank Check” can be left unselected).

The programming runs in four phases:

  • Programming the FPGA with a bitfile that turns the FPGA into a programmer for its flash
  • “Step 1″: Erasing the FPGA
  • “Step 2″: Writing to the FPGA
  • “Step 3″: Verifying

All in all, 15 minutes for a 7V330 device isn’t unusual.

 

ALSA’s file plugin for playing back a raw pipe file

Motivation

On an embedded system, I have a device file /dev/xillybus_audio, which can be opened for read and/or write. One can write raw (signed 16 bit Little Endian Rate 48000 Hz stereo) samples to this file, and they’re played on a “headphones out” plug, and one can read samples of the same time, which are captured from a “mic input” plug. Clean and simple. Now let’s use that as an ALSA sound interface. This is where it doesn’t get all that simple.

How about a kernel driver for that interface? Nice idea, but the stream interface is already there. Besides, this is useful for piping with programs etc.

Attempt I

To make along story short, making /etc/asound.conf read like this, and playing back works (but capturing doesn’t!).

pcm.xillybus {
    type asym
    playback.pcm {
        type plug
        slave {
            pcm {
                type file
                file "/dev/xillybus_audio"
                slave.pcm null
                format raw
            }
            rate 48000
            format s16_le
            channels 2
        }
    }
    capture.pcm {
        type plug
        slave {
            pcm {
                type file
                file "/dev/null"
                infile "/dev/xillybus_audio"
                slave.pcm null
            }
            rate 48000
            format s16_le
            channels 2
        }
    }
}

Playback works with rates other than 48000 Hz (and other formats), because of the wrapping with the “plug” plugin.

# aplay -D "xillybus" rate8000.wav
Playing WAVE 'rate8000.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo

Note that “file” — which defines the output file — must be defined or arecord (or whatever program is used) quits on a segmentation fault. Not very polished.

Capturing doesn’t work at all, however. It’s just a silence file, which grows way too fast. It has been said that the slave of the capturing device shouldn’t be null, and indeed this probably the issue.

Diving into it

The problem seems to lie in the implementation of the file capture routine. Taken from alsa-lib-1.0.27.2/src/pcm/pcm_file.c:

static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
{
    snd_pcm_file_t *file = pcm->private_data;
    snd_pcm_channel_area_t areas[pcm->channels];
    snd_pcm_sframes_t n;

    n = snd_pcm_readi(file->gen.slave, buffer, size);
    if (n <= 0)
         return n;
    if (file->ifd >= 0) {
        n = read(file->ifd, buffer, n * pcm->frame_bits / 8);
        if (n < 0)
             return n;
        return n * 8 / pcm->frame_bits;
    }
    snd_pcm_areas_from_buf(pcm, areas, buffer);
    snd_pcm_file_add_frames(pcm, areas, 0, n);
    return n;
}

This is the method, which the plugin exposes for reading samples. Note that it attempts to read the desired amount of samples from the slave first, and then attempts to fetch the same number of samples it got from the slave, from the file. This probably makes sense when reading from a plain file, because it would otherwise slurp the entire file in no-time. The slave is used as a data rate controller. Great.

Attempt II

To come around this, I changed /etc/asound.conf to this:

pcm.xillybus_raw {
                type file
                file "/dev/xillybus_audio"
                slave.pcm null
                format raw
}
pcm.xillybus_play {
        type plug
        slave {
            pcm "xillybus_raw"
            rate 48000
            format s16_le
            channels 2
        }
}

pcm.xillybus {
    type asym
    playback.pcm "xillybus_play"
    capture.pcm {
        type plug
        slave {
            pcm {
                type file
                file "/dev/null"
                infile "/dev/xillybus_audio"
                slave.pcm "xillybus_play"
            }
            rate 48000
            format s16_le
            channels 2
        }
    }
}

This isn’t perfect either. When attempting

# arecord -D "xillybus" --rate 48000 --channels 2 --format s16_le try.wav

sound is indeed recorded into try.wav. The captured sound is echoed in the headphones (with a delay), so the output interface is now busy and noisy. But worst of all, this only works if the parameters are set exactly to the sound interface’s. So I could have read directly from /dev/xillybus_audio as well.

Changes in pcm_file.c

Based upon alsa-lib-1.0.25, the following functions were changed in pcm_file.c. The intention of these changes is to detach the I/O operations from the slave, which is null in the setting of Attempt I above.

static int snd_pcm_file_drop(snd_pcm_t *pcm)
{
	return 0;
}

static int snd_pcm_file_drain(snd_pcm_t *pcm)
{
	return 0;
}

static snd_pcm_sframes_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
{
	snd_pcm_file_t *file = pcm->private_data;
	snd_pcm_channel_area_t areas[pcm->channels];
	snd_pcm_sframes_t n;

	n = read(file->ifd, buffer, size * pcm->frame_bits / 8);
	if (n < 0)
		return n;
	return n * 8 / pcm->frame_bits;
}

static snd_pcm_sframes_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
	snd_pcm_file_t *file = pcm->private_data;
	snd_pcm_channel_area_t areas[pcm->channels];
	snd_pcm_sframes_t n;

	SNDERR("DEBUG: Noninterleaved read not yet implemented.\n");
	return 0;	/* TODO: Noninterleaved read */
}

(these functions don’t appear one after the other in the source file)

Compiling to obtain libasound.so

After making the changes in pcm_file.c, compiled natively on the embedded board

# ./configure
# make -j 2

and then copy the result to the library directory:

# cp src/.libs/libasound.so.2.0.0 /usr/lib/arm-linux-gnueabihf/

This overwrites the previous file.

Plugin library issue

When attempting to use a sound interface with the new libasound, the following error occurs:

# aplay -D "xillybus" snip.wav
ALSA lib conf.c:3314:(snd_config_hooks_call) Cannot open shared library libasound_module_conf_pulse.so
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM xillybus
aplay: main:682: audio open error: No such file or directory

this is because the plugin loader looks in the wrong directories: It does look in /usr/lib/arm-linux-gnueabihf/, but not in /usr/lib/arm-linux-gnueabihf/alsa-lib/.

Trying configure parameters didn’t help:

# ./configure --libdir=/usr/lib/arm-linux-gnueabihf/
# ./configure --with-plugindir=/usr/lib/arm-linux-gnueabihf/alsa-lib/

The dirty solution was to create symbolic links to all files in alsa-lib/ that aren’t symbolic links themselves with

# cd /usr/lib/arm-linux-gnueabihf
# for i in `find alsa-lib/ -type f -a ! -type l` ; do ln -s "$i" ; done

Not the most elegant solution, but after spending a couple of hours on trying to figure this out, at least it works.

This is the list of files that were symlinked:

alsa-lib/libasound_module_pcm_speex.so
alsa-lib/libasound_module_ctl_oss.so
alsa-lib/libasound_module_ctl_pulse.so
alsa-lib/libasound_module_pcm_usb_stream.so
alsa-lib/libasound_module_pcm_pulse.so
alsa-lib/libasound_module_rate_samplerate.so
alsa-lib/libasound_module_ctl_bluetooth.so
alsa-lib/libasound_module_pcm_jack.so
alsa-lib/libasound_module_pcm_upmix.so
alsa-lib/libasound_module_pcm_bluetooth.so
alsa-lib/libasound_module_conf_pulse.so
alsa-lib/libasound_module_pcm_oss.so
alsa-lib/libasound_module_rate_speexrate.so
alsa-lib/libasound_module_ctl_arcam_av.so
alsa-lib/libasound_module_pcm_vdownmix.so
alsa-lib/smixer/smixer-sbase.so
alsa-lib/smixer/smixer-ac97.so
alsa-lib/smixer/smixer-hda.so

Current position

Both record and playback work with the first asound.conf above (a.k.a. Attempt I), as long as the parameters for recording are the same. For playback, the parameters must be the 48000 Hz, s16_le but it’s fine to work in mono and stereo. If other parameters are attempted, a huge file which is filled with click sounds is created.

So

# arecord -D "xillybus" --rate 48000 --format s16_le --channels 2 good.wav
Recording WAVE 'good.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo

makes a nice sound file, but

# arecord -D "xillybus" --format s16_le --channels 2 justclicks.wav
Recording WAVE 'justclicks.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo

creates a huge file with just clicks. The intriguing thing about those failing just-click scenarios, is that snd_pcm_file_readi() is never called when this happens. It looks like something in the data flow goes wrong when a rate resampler is pushed into the system. When the rate is the same, snd_pcm_file_readi() has been observed to be called in a steady way.

Both of the following are OK:

# aplay -D "xillybus" good.wav
Playing WAVE 'good.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo
# aplay -D "xillybus" rate8000.wav
Playing WAVE 'rate8000.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo

 

ALSA and Pulseaudio random jots

Background

This is just things I wrote down while trying to make aplay play sound through a fake module-based Pulseaudio sink. Spoiler: I failed.

Spoiler II: Sometimes people ask me question about posts I write. I don’t think I’ll answer any on this post. I’ll probably forget all about this five minutes from now.

So –

It all worked fine as long as there was only one of those sinks, because it was set to the default of Pulseaudio, and apparently aplay was ready to consider Pulseaudio as a sound card, playing sound to whatever the user configured as its sink (on Pulseaudio’s configuration interface). But what if I wanted two of those at the same time. Problem.

The following segment in /etc/pulse/default.pa indeed creates two sources and sinks in Pulseaudio’s environment:

load-module module-file-sink file=/dev/xillybus_audio rate=48000
load-module module-file-source file=/dev/xillybus_audio rate=48000
load-module module-file-sink file=/dev/xillybus_audio2 rate=48000
load-module module-file-source file=/dev/xillybus_audio2 rate=48000

Using “pacmd list-sources” and “pacmd list-sinks”, I have found Pulseaudio’s names for these, so that it’s possible to record and play with

parecord -d 'fifo_input' --file-format=wav --rate=44100 > junk.wav

or

parecord -d 'fifo_input.2' --file-format=wav --rate=44100 > junk.wav

and then play the sound back with

paplay -d 'fifo_output' junk.wav

or

paplay -d 'fifo_output.2' junk.wav

And there’s always the possibility to fake old-style /dev/dsp devices with padsp, and use them with whatever application that work with these.

ALSA random jots

/usr/share/alsa/alsa.conf defines the well-known PCM names “hw:” included, as pcm.hw { … } and also lists the other files to look at. So it’s definitely the place to start looking for understanding those name conventions. Or maybe the ‘hw:” prefix is no more than a reference to the ALSA hw plugin…?

The device’s name is used in aplay when in calls snd_pcm_open(), which is an alsa-lib call. In other words, aplay doesn’t use Pulseaudio in this case.

It’s part of alsa-lib, in src/pcm/pcm.c. This function calls snd_pcm_open_noupdate(), which in turn calls snd_config_search_definition() with the device name. If it returns with an error, we get the

ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM fifo_input
arecord: main:682: audio open error: No such file or directory

snd_config_search_definition() is defined in src/conf.c. It strips off anything after a ‘:’ (if such exists) and calls snd_config_search_alias_hooks() with the stripped name. So crucial question seems to be how to convince pulseaudio to inject an alias to the file sink, as it does when it’s the default.

It looks like snd_device_name_hint() is useful (supersedes the deprecated snd_names_list() in src/names.c). The following C code snippet (found here)

  {
    char **hints;
    /* Enumerate sound devices */
    int err = snd_device_name_hint(-1, "pcm", (void***)&hints);
    if (err != 0)
      return;//Error! Just return

    char** n = hints;
    while (*n != NULL) {

      char *name = snd_device_name_get_hint(*n, "NAME");

      if (name != NULL && 0 != strcmp("null", name)) {
	printf("Name hint: %s\n", name);
        free(name);
      }
      n++;
    }

    snd_device_name_free_hint((void**)hints);
  }

prints out (on my PC, note that the “hw” name isn’t listed).

Name hint: default
Name hint: front:CARD=Intel,DEV=0
Name hint: surround40:CARD=Intel,DEV=0
Name hint: surround41:CARD=Intel,DEV=0
Name hint: surround50:CARD=Intel,DEV=0
Name hint: surround51:CARD=Intel,DEV=0
Name hint: surround71:CARD=Intel,DEV=0
Name hint: iec958:CARD=Intel,DEV=0
Name hint: hdmi:CARD=HDMI

BTW, when Pulseaudio is shut down on a machine where only “default” is listed (no ALSA cards), “default” goes away, and there’s “pulse” instead.

This page talks about ALSA tweaking.

The format of the asound.conf file is described on this page. The C library reference, mentioning the naming convention is here.

Pulseaudio jot

Download pulseaudio’s source with

git clone git://anongit.freedesktop.org/pulseaudio/pulseaudio

An interesting function is pa_alsa_source_new() in src/modules/alsa/alsa-source.c.

Running JavaScript code from the command line

Sometimes, there are JavaScript snippets for the sake of obfuscation (hmmm, including this site). This is code made complicated intentionally, to prevent web spiders from harvesting addresses or emails. But hey, what if I’m the one with the spider?

The simple, and somewhat dangerous solution, is to run the JavaScript code on a local interpreter. I found Google’s V8 project most suitable for this purpose. Download the sources from Google’s SVN:

$ svn checkout http://v8.googlecode.com/svn/trunk/ v8

Following the instructions for building with GYP, change directory to v8/ and download GYP (and other stuff, I suppose)

$ make dependencies

And build for the current platform:

$ time make -j 8 native

which fails, because warnings are treated as errors (on GCC 4.4.4). So this instead:

$ time make werror=no -j 8 native

This worked, and took 2.30 minutes on my computer. The outputs go to out/native, so

$ cd out/native/
$ ./d8
V8 version 3.26.12 [console: dumb]
d8> print("Hello, world");
Hello, world
undefined

Isn’t that sweet? It just executes the command.

Note that d8 always returns the value of the last operation, which is nice when all we want is evaluation an obfuscated expression.

d8> os.system("date");
"Sun Apr 13 18:59:24 IDT 2014
"

Ayyeee! The interpreter allows shelling out! This means that running an alien script on our machine is extremely dangerous: If a spider is calling the interpreter with scripts that it retrieves from the web, one could easily contain code that attempts to run code on the host’s computer, if it detects that the environment isn’t a browser. Protective measures aren’t simple. I don’t know of any way to safely prevent the interpreter from accessing its host’s capabilities, except for applying seLinux or (the weaker option) chroot jailing. Or maybe use Linux namespaces for lightweight virtualization.

Anyhow, there are other executables created as well, for example, “shell”:

$ ./shell
V8 version 3.26.12 [sample shell]
> print("Hello, world");
Hello, world
>

More info can be found on this page. For example, it’s possible to quit the shell with the quit(0) command.

Vacuum your CPU: When cooling suddenly doesn’t work anymore

So I compiled a Linux kernel with 8 threads in parallel on my Linux desktop machine, as I always do. The CPU worked extra hard as usual, but lately its temperature began to rise, ending up at 88°C. It looks like a clock gating mechanism kicked in to save the CPU.

But hey, this never happened in the past! Asking a round a bit, I was advised to check if the fan is OK. Maybe the thermal paste went dry.

Opening the case and looking, I noticed that the heatsink was full with dust. More precisely, a lot of dust was stuck between the heatsink’s grill blades, obstructing the air flow. No air flow, no cooling. So I unsnapped the fan off the heatsink, took a vacuum cleaner, and removed all dust.

And my PC is like new now! The temperature goes from 30.0°C to no more than 44.0°C when I run that kernel compilation test (watching the temperature with “watch sensors” at shell prompt).

It was that simple.

Note to self: Vacuum the CPU’s heatsink every now and then.

And here’s what it looks like after two years, during which the computer has been on continuously (click on images to enlarge):

Heat sink and fan, before cleaning up dust

And this is with the fan taken off. One can clearly see that the layer of dust disrupts the air flow.

Heat sink,  fan taken off, before cleaning up dustA minute with the vacuum cleaner, and we have

Heat sink,  fan taken off, after cleaning up dustSnap the fan back in place, and the computer is ready to go!

Heat sink + fan taken, after cleaning up dust, ready to go

 

List all files in a subdirectory, with the SHA1 sums

Useful for diffing two sets of filesystems, just to see where the changes are (and maybe catch a file that was accidentally copied in)

Symbolic links and other non-regular files are ignored. If they’ve changed, there is no alarm on these.

The script (the path to the directory to be scanned is given as an argument):

#!/bin/bash

[ -d "$1" ] || exit 1;
cd $1 || exit 1;

find . | while read i; do
 if [ -f "$i" ] && [ ! -h "$i" ]; then
   sha1sum "$i";
 else
   echo "----------------------------------------  $i";
 fi
done

To sort the output:

$ sort -k 1.41 list-of-files.txt > sorted.txt

The -k parameter causes sort to work from character 41 and on. Otherwise it would sort according to the SHA1 sums.

“Unsupported machine ID” after upgrading Linux kernel or U-boot

Unlike how I usually treat software tools I work with, my attitude towards U-boot is “if it works, never mind how and why”. Trying to understand the gory details of U-boot has never been very rewarding. Things work or break more or less randomly, depending on which git revision is checked out. Someone sent a patch fixing this and breaking that, and then someone else sent another patch.

So this is my story: After upgrading the Linux kernel from 3.3 to 3.12 (both having a strong Xilinx flavor, as the target is a Zynq board), but kept an old version of U-boot, I ran through the drill that usually makes the kernel boot:

zynq-uboot> fatload mmc 0 0x8000 zImage
reading zImage

2797680 bytes read
zynq-uboot> fatload mmc 0 0x1000000 devicetree.dtb
reading devicetree.dtb

5827 bytes read
zynq-uboot> go 0x8000
## Starting application at 0x00008000 ...

Error: unrecognized/unsupported machine ID (r1 = 0x1fb5662c).

Available machine support:

ID (hex)        NAME
ffffffff        Generic DT based system
ffffffff        Xilinx Zynq Platform

Please check your kernel config and/or bootloader.

So the kernel didn’t boot. Looking at the way I attempted to kick it off, one may wonder how it worked at all with kernel v3.3. But one can’t argue with the fact that it used to boot.

The first thing to understand about this error message, is that it’s fatally misleading. The real problem is that the device tree blob isn’t found by the kernel, so it reverts to looking for a machine ID in r1. And r1 just has some value. The error message comes from a piece of boot code that is never reached, if the device tree is found and used.

Now, let’s try to understand the logic behind the sequence of commands: The first command loaded the zImage into a known place in memory, 0x8000. One could ask why I didn’t use uImage. Well, why should I? zImage works, and that’s the classic way to boot a kernel.

The device tree blob is then loaded to address 0x10000000.

And then comes the fun part: U-boot just jumps to 0x8000, the beginning of the image. I think I recall that one can put zImage anywhere in memory, and it will take it from there.

But how does the kernel know that the device tree is at 0x10000000? Beats me. I suppose it’s hardcoded somewhere. But hey, it worked! At least on older kernels. And on U-boot 2012.10 and older (but not 2013.10).

For the newer kernel (say, 3.12), a completely different spell should be cast. Something like this (using U-Boot 2012.10 or 2013.10):

zynq-uboot> fatload mmc 0 0x3000000 uImage
reading uImage                                                                  

3054976 bytes read
zynq-uboot> fatload mmc 0 0x2A00000 devicetree.dtb
reading devicetree.dtb                                                          

7863 bytes read
zynq-uboot> bootm 0x3000000 - 0x2A00000
## Booting kernel from Legacy Image at 03000000 ...
   Image Name:   Linux-3.12.0-1.3-xilinx
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    3054912 Bytes = 2.9 MiB
   Load Address: 00008000
   Entry Point:  00008000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 02a00000
   Booting using the fdt blob at 0x02a00000
   Loading Kernel Image ... OK
OK
   Loading Device Tree to 1fb4f000, end 1fb53eb6 ... OK                         

Starting kernel ...                                                             

Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0x0
[ ... kernel boots, bliss and happiness ... ]

OK, so I twisted it all around. All of the sudden, I use an uImage, rather than zImage. Why? Because bootm works with uImage, and bootz wasn’t supported by that specific U-boot version (or configuration, I’m not really sure).

The addresses I loaded into are different too, and I’m not sure it matters. What surely matters, is that bootm was explicitly given the address of the device tree blob, and therefore passed that through a register to the kernel. So in this case, it’s pretty obvious how the kernel finds what’s where.

Ah, but there’s a twist. The latter, bootm-based method didn’t work on the 3.3 kernel. In fact, I got a very similar error message when I tried that.

As I said in the beginning, I never cared dive deep into U-boot. So all I’m saying is — if you encounter an error message like the one above, just try the other magic spell, and hope for good.

And if someone known more about what happened in the connection between U-boot and Linux somewhere in 2012, or has a link to some mailing list discussion where this was decided upon, please comment below. :)

Linux kernel platform device food chain example

Since the device tree is the new way to set up hardware devices on embedded platforms, I hoped that I could avoid the “platform” API for picking which driver is going to take control over what. But it looks like the /arch/arm disaster is here to stay for a while, so I need to at least understand how it works.

So for reference, here’s an example walkthrough of the SPI driver for i.MX51, declared and matched with a hardware device.

The idea is simple: The driver, which is enabled by .config (and hence the Makefile in its directory includes it for compilation) binds itself to a string during its initialization. On the other side, initialization code requests a device matching that string, and also supplies some information along with that. The example tells the story better.

The platform API is documented in the kernel tree’s Documentation/driver-model/platform.txt. There’s also a nice LWN article by Jonathan Corbet.

So let’s assume we have Freescale’s 3-stack board at hand. in arch/arm/mach-mx5/mx51_3stack.c, at the bottom, it says

MACHINE_START(MX51_3DS, "Freescale MX51 3-Stack Board")
 .fixup = fixup_mxc_board,
 .map_io = mx5_map_io,
 .init_irq = mx5_init_irq,
 .init_machine = mxc_board_init,
 .timer = &mxc_timer,
MACHINE_EN

mxc_board_init() is defined in the same file, which among many other calls goes

mxc_register_device(&mxcspi1_device, &mxcspi1_data);

with the extra info structure mxcspi1_data defined as

static struct mxc_spi_master mxcspi1_data = {
 .maxchipselect = 4,
 .spi_version = 23,
 .chipselect_active = mx51_3ds_gpio_spi_chipselect_active,
 .chipselect_inactive = mx51_3ds_gpio_spi_chipselect_inactive,
};

Now to the declaration of mxcspi1_device: In arch/arm/mach-mx5/devices.c we have

struct platform_device mxcspi1_device = {
	.name = "mxc_spi",
	.id = 0,
	.num_resources = ARRAY_SIZE(mxcspi1_resources),
	.resource = mxcspi1_resources,
	.dev = {
		.dma_mask = &spi_dma_mask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
	},
};

and before that, in the same file there was:

static struct resource mxcspi1_resources[] = {
	{
		.start = CSPI1_BASE_ADDR,
		.end = CSPI1_BASE_ADDR + SZ_4K - 1,
		.flags = IORESOURCE_MEM,
	},
	{
		.start = MXC_INT_CSPI1,
		.end = MXC_INT_CSPI1,
		.flags = IORESOURCE_IRQ,
	},
	{
		.start = MXC_DMA_CSPI1_TX,
		.end = MXC_DMA_CSPI1_TX,
		.flags = IORESOURCE_DMA,
	},
};

So that defines the magic driver string and the resources that are allocated to this device.

It’s worth noting that devices.c ends with

postcore_initcall(mxc_init_devices);

which causes a call to mxc_init_devices(), a function that messes up the addresses of the resources for some architectures. Just to add some confusion. Always watch out for those little traps!

Meanwhile, in drivers/spi/mxc_spi.c

static struct platform_driver mxc_spi_driver = {
	.driver = {
		   .name = "mxc_spi",
		   .owner = THIS_MODULE,
		   },
	.probe = mxc_spi_probe,
	.remove = mxc_spi_remove,
	.suspend = mxc_spi_suspend,
	.resume = mxc_spi_resume,
};

followed by:

static int __init mxc_spi_init(void)
{
	pr_debug("Registering the SPI Controller Driver\n");
	return platform_driver_register(&mxc_spi_driver);
}

static void __exit mxc_spi_exit(void)
{
	pr_debug("Unregistering the SPI Controller Driver\n");
	platform_driver_unregister(&mxc_spi_driver);
}

subsys_initcall(mxc_spi_init);
module_exit(mxc_spi_exit);

So this is how the driver tells Linux that it’s responsible for devices marked with the “mxc_spi” string.

As for some interaction with the device data (also in mxc_spi.c), there’s stuff like

mxc_platform_info = (struct mxc_spi_master *)pdev->dev.platform_data;

and

master_drv_data->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

going on with

if (!request_mem_region(master_drv_data->res->start,
			master_drv_data->res->end -
			master_drv_data->res->start + 1, pdev->name)) { /* Ayee! */ }

and

if (pdev->dev.dma_mask == NULL) { /* No DMA for you! */ }

and it goes on…

Reading the DocBook files in Linux kernel’s documentation

This is my short saga about my not necessarily intelligent actions for reading a DocBook paper.

So I wanted to read some documentation from my Linux kernel sources. It happened to be in DocBook format.

In the kernel source’s root, I tried

$ make htmldocs

or I could have gone

$ make pdfdocs

Or mandocs. Or sgmldocs. Or psdocs.

But that builds only the DocBook templates in Documentation/DocBook/. I need those in Documentation/sound/alsa/DocBook!

I tried to copy the Makefile to the target directory, and change the assignment of DOCBOOKS to the files I wanted handled. But that didn’t work, because the Makefile couldn’t find the script/ subdirectory. Or as “make” put it:

make: *** No rule to make target `/scripts/kernel-doc', needed by `/alsa-driver-api.tmpl'.  Stop.

OK, this was a bit too much. How about just going…

$ docbook2html writing-an-alsa-driver.tmpl

Works, creates a lot of scattered HTML files. Not so easy to read. Can’t I get it in a single document?

$ docbook2rtf writing-an-alsa-driver.tmpl

Huh? WTF? RTF? Yes, it’s apparently still alive. And hey, one can easily export it to PDF with OpenOffice!

This probably isn’t exactly the way the kernel hackers meant it to be done. On the other hand, it’s by far too much effort for just reading a document by and for the community…

I’m sure someone knowns about the obvious way I missed. Comments below, please…