diff --git a/util/plot_shmoo.py b/util/plot_shmoo.py index cdcc0f8..b5d8711 100755 --- a/util/plot_shmoo.py +++ b/util/plot_shmoo.py @@ -92,6 +92,13 @@ def generate_data( def main(out_file: str, *genargs) -> int: + # CLI invocations cannot pass real `None`; the documented usage in this + # file's header uses positional placeholders for the optional measurement + # arguments, which then arrive here as the literal string "None". Convert + # those (and empty strings) to actual `None` so the downstream + # `is not None` checks behave as intended. + genargs = tuple(None if g in ('None', '') else g for g in genargs) + # Generate data to be plotted data = generate_data(*genargs) @@ -99,13 +106,13 @@ def main(out_file: str, *genargs) -> int: bar_cmap = mpl.colormaps['viridis'] bar_cmap.set_under('white') bar_show = True - if genargs[6] is not None: + if len(genargs) > 6 and genargs[6] is not None: bar_legend = 'En. Eff. (MFLOP/s/W)' bar_data = data['effs_mflop_per_s_per_w'] - elif genargs[4] is not None: + elif len(genargs) > 4 and genargs[4] is not None: bar_legend = 'Energy (mJ)' bar_data = data['es_mj'] - elif genargs[1] is not None: + elif len(genargs) > 1 and genargs[1] is not None: bar_legend = 'Power (mW)' bar_data = data['ps_mw'] else: @@ -119,22 +126,31 @@ def main(out_file: str, *genargs) -> int: fig = plt.figure(figsize=(3.3*scale, 2.0*scale)) ax = plt.subplot(111) - # Style axes - xdata = data['vs_v'] - ydata = data['fs_mhz'] - xticks = xdata[::5] - yticks = ydata[::6] + # Style axes. The plotted axes are frequency on X (matching xlabel) and + # voltage on Y (matching ylabel); name the local variables accordingly so + # the tick wiring below cannot drift from the pcolormesh argument order. + freq_mhz = data['fs_mhz'] + volt_v = data['vs_v'] + freq_ticks = freq_mhz[::3] + volt_ticks = volt_v[::5] ax.set_xlabel('Frequency (MHz)', fontsize=10) ax.set_ylabel('Core Voltage (V)', fontsize=10) - plt.xticks(xticks, rotation=0) - ax.set_yticks(yticks) - ax.set_xticklabels(xticks) - ax.set_yticklabels(yticks) - ax.xaxis.set_major_formatter(lambda val: f'{val:.0f}') - ax.yaxis.set_major_formatter(lambda val: f'{val:1.2f}') - - # Plot the desired data and save - c = ax.pcolormesh(ydata, xdata, bar_data, cmap=bar_cmap, edgecolor='silver', linewidth=0.0) + ax.set_xticks(freq_ticks) + ax.set_yticks(volt_ticks) + ax.set_xticklabels([f'{v:.0f}' for v in freq_ticks]) + ax.set_yticklabels([f'{v:.2f}' for v in volt_ticks]) + # Matplotlib >= 3.5 calls the formatter as ``fmt(value, pos)``; the + # original 1-arg lambdas raised TypeError during draw, preventing the + # plot from ever being saved. + ax.xaxis.set_major_formatter(lambda val, _pos: f'{val:.0f}') + ax.yaxis.set_major_formatter(lambda val, _pos: f'{val:1.2f}') + + # Plot the desired data and save. pcolormesh(X, Y, C): X = frequency, + # Y = voltage. `bar_data` has shape (len(volt_v), len(freq_mhz)) so + # `bar_data[volt_idx][freq_idx]` lands at (freq_mhz[freq_idx], + # volt_v[volt_idx]). + c = ax.pcolormesh(freq_mhz, volt_v, bar_data, cmap=bar_cmap, + edgecolor='silver', linewidth=0.0) if bar_show: cbar = fig.colorbar(c, ax=ax) cbar.set_label(bar_legend, fontsize=10)