Automatic mount stops after kernel upgrade and sysfs

I really have this thing about backward compatibility, which is why I chose to enable the CONFIG_SYSFS_DEPRECATED and CONFIG_SYSFS_DEPRECATED_V2 kernel flags when compiling kernel 3.12 for Fedora 12. After all, an old distribution with a new kernel.

This turned out to be wrong: The distribution isn’t all that old, and automounting stopped to work for USB SD cards (and possibly other stuff) as a result of this. Even though running the HAL daemon (an oldie, yes…) with debug info

# hald --daemon=no --verbose=yes

showed that it detected the insertion of the USB device. And still, no automount.

On my system, two things happen when CONFIG_SYSFS_DEPRECATED and CONFIG_SYSFS_DEPRECATED_V2 are enabled:

The directories for block devices for hard disks disappear, e.g. /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda and also those for non-disk devices, e.g. /sys/devices/virtual/block/md0/ and /sys/devices/virtual/block/ram0.

Instead, they appear as e.g. /sys/block/sda, with a completely different outline of files. According to the Kconfig help, the parameters that are exposed are more prone to changes over time. In the non-deprecated format, there’s a symbolic link to the respective directory in /sys/devices/ instead of the full-blown directories that appear there in the deprecated mode.

So the CONFIG_SYSFS_DEPRECATED_V2 enables this feature by default, which was a mistake. To spare myself the kernel recompilation, I added sysfs.deprecated=0 to the kernel command line, and things went back to normal.

VMware Player or Workstation: Patching for Linux kernel 3.12 (or so)

For a reason not so clear to me, VMware doesn’t keep its drivers up to date with newer kernels, so they fail to compile against newer kernels. Consequently, there’s an insane race to patch them up. It starts with a compilation failure at the GUI level, and sooner or later it becomes clear that there’s no choice but to get down to work.

The procedure, if you don’t want to install VMware on the computer you’re working on, is to first extract the files with something like (as a non-root user)

$ bash VMware-Player-6.0.2-1744117.x86_64.bundle --extract newplayer

which just writes the files into a new directory newplayer/.

Next step is to untar the relevant directories into a fresh working directory. May I suggest:

for i in /path/to/newplayer/vmware-vmx/lib/modules/source/*.tar ; do tar -xvf $i ; done

This creates five new directories, each containing a Makefile and kernel module sources. In principle, the goal is to type “make” in all five and not have an error.

That’s where the headache is. I’ve packed up a set of patches that took me from Player 6.0.2 (or Workstation 10.0.2) to kernel 3.12 (in a rather messy way, but hey, nothing about it is really in order): Here they are as a tarball.

Once that is done, wrap it all up with

$ for i in vmblock vmci vmmon vmnet vsock ; do tar -cf $i.tar $i-only ; done

and copy the *.tar files into /usr/lib/vmware/modules/source/ (actually, replace the existing files) in an existing installation.

And then just run VMware as usual, and behave nicely when it wants to install modules.

Or if you want to see it with your own eyes (as root):

# vmware-modconfig --console --install-all

VMCI issues

As if it wasn’t enough as is, there was a problem with running the VMCI:

Starting VMware services:
 Virtual machine monitor                                 [  OK  ]
 Virtual machine communication interface                 [FAILED]
 VM communication interface socket family                [  OK  ]
 Blocking file system                                    [  OK  ]
 Virtual ethernet                                        [  OK  ]
 VMware Authentication Daemon                            [  OK  ]

And of course, so virtual machine would agree to run this way.

After a lot of messing around, it turned out that for some reason, the module wasn’t installed. Go figure.

The solution was to go back to the vmci-only directory (one of the untarred one above), compile it with “make” (it should work by now, after all), and then copy it to the running kernel’s module repository:

# cp vmci.ko /lib/modules/$(uname -r)/kernel/drivers/misc/
# depmod -a

Or maybe just create a kernel/drivers/vmware directory and copy all *.ko files that were compiled, and depmod.

I. Never. Want. To hear. About. This. Again.

Kernel compilation without extra “+” or other markers in the version string

So there’s this “+” sign added to the kernel version (as displayed with uname -r) when the kernel is compiled with a git tree that doesn’t sit on an official version (or more precisely, not on an annotated tag). Which kinda makes sense to tell the kernel’s users that the kernel isn’t exactly the vanilla thing. Only it annoys me. Partly because modules, which are compiled under the kernel headers that are extracted from this kernel (if stored separately) will not match — their version will lack the “+” sign, since there is no git repo attached to the headers.

For a while I used to move the .git directory to .gitt before compiling, and then back again afterwards to prevent this plus sign from appearing on my kernels. And then I decided to really fix it.

I’d also mention that assigning KERNELVERSION on the make command doesn’t prevent the “+” sign.

So this is what to do: Edit scripts/setlocalversion and add the line marked in red, somewhere around line 38.

[ ... ]

scm_version()
{
 local short
 short=false

 cd "$srctree"

 return; # Do nothing.

  [ ... ]
}

That’s it. The git’s state is ignored, no more “+” anymore. The trick is to return immediately from the function that is supposed to add these extra version markers as necessary. So it doesn’t get the chance.

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.