[igt-dev] [PATCH i-g-t v5] tools: Add an intel_dbuf_map tool
Juha-Pekka Heikkilä
juha-pekka.heikkila at intel.com
Mon Dec 30 14:18:21 UTC 2019
just some small comments below on the code. I'll need to run the tool
somewhere to give more meaningful comments.
/Juha-Pekka
On 30.12.2019 11.07, Stanislav Lisovskiy wrote:
> As in modern platforms more Display buffer
> slices/pipes are coming, which have different
> pipe affinities and other constraints, so BSpec
> contains their connection in form of a Graph.
>
> So we can generate optimal DBuf assignment,
> from the graph which we have in BSpec, to avoid
> manual calculations prone to human error and
> copy-pasting. The generated table is in C form
> and can be used in i915 driver rightaway and also
> for verification.
>
> v2: Removed unused i, j, made some functions static,
> to make CI checkers a bit happier. Also had to
> change IGT_LIST to IGT_LIST_HEAD and other stuff,
> as functions seem to be renamed in latest master.
>
> v3: Added command line arguments "--graph" and
> "slices_per_pipe" which allow to define and
> get DBuf assignment from graph without recompiling
> the tool, but right away.
> Example usage:
> intel_dbuf_map --graph PIPE_A-DBUF_S1-DBUF_S2-PIPE_B-PIPE_C --slices_per_pipe 2
>
> Generates output:
> Graph:
> Type PIPE id 0
> (1)->
> Type DBUF_SLICE id 0
> (1)->
> Type DBUF_SLICE id 1
> (1)->
> Type PIPE id 1
> (1)->
> Type PIPE id 2
> Combination with least weight 3(total combinations 1):
> { BIT(PIPE_A), { DBUF_S1_BIT | DBUF_S2_BIT, 0, 0, 0 } },
> Combination with least weight 3(total combinations 1):
> { BIT(PIPE_B), { 0, DBUF_S2_BIT | DBUF_S1_BIT, 0, 0 } },
> Combination with least weight 2(total combinations 2):
> { BIT(PIPE_A) | BIT(PIPE_B), { DBUF_S1_BIT, DBUF_S2_BIT, 0, 0 } },
> Combination with least weight 5(total combinations 1):
> { BIT(PIPE_C), { 0, 0, DBUF_S2_BIT | DBUF_S1_BIT, 0 } },
> Combination with least weight 3(total combinations 2):
> { BIT(PIPE_A) | BIT(PIPE_C), { DBUF_S1_BIT, 0, DBUF_S2_BIT, 0 } },
> Combination with least weight 4(total combinations 2):
> { BIT(PIPE_B) | BIT(PIPE_C), { 0, DBUF_S2_BIT, DBUF_S1_BIT, 0 } },
> Combination with least weight 4(total combinations 6):
> { BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_C), { DBUF_S1_BIT, DBUF_S2_BIT, DBUF_S2_BIT, 0 } },
>
> v4: - Minor refactoring, simplified algorithm, removed redundant list
> usage
> - Fixed title
> - Added debug option
> - Removed unneeded ARRAY_SIZE definition, which already exists
> (Juha-Pekka Heikkilä)
>
> v5: - Switched to named pipe array initialization, to match current
> kernel code.
> - Minor code refactoring.
>
> Signed-off-by: Stanislav Lisovskiy <stanislav.lisovskiy at intel.com>
> Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
> ---
> tools/intel_dbuf_map.c | 884 +++++++++++++++++++++++++++++++++++++++++
> tools/meson.build | 1 +
> 2 files changed, 885 insertions(+)
> create mode 100644 tools/intel_dbuf_map.c
>
> diff --git a/tools/intel_dbuf_map.c b/tools/intel_dbuf_map.c
> new file mode 100644
> index 00000000..ac42f608
> --- /dev/null
> +++ b/tools/intel_dbuf_map.c
> @@ -0,0 +1,884 @@
> +/*
> + * Copyright © 2019 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + * Stanislav Lisovskiy <stanislav.lisovskiy at intel.com>
> + *
> + */
> +
> +#include <stdlib.h>
> +#include "igt.h"
> +
> +enum NodeType {
> + DBUF_SLICE,
> + PIPE
> +};
> +
> +const char *node_type_to_str[] = { "DBUF_SLICE", "PIPE" };
> +
> +#define MAX_CONNECTIONS 10
> +#define MAX_NODES 10
> +#define MAX_EDGES (MAX_NODES * 2)
> +
> +struct GraphEdge;
> +
> +/*
> + * Graph node, which can be DBuf or Pipe
> + */
> +struct GraphNode {
> + int id;
> + enum NodeType type;
> + unsigned int use_count;
> + int visited;
> + struct GraphEdge *connections[MAX_CONNECTIONS];
> + int num_connections;
> +};
> +
> +static struct GraphNode *all_nodes[MAX_NODES];
> +static struct GraphEdge *all_edges[MAX_EDGES];
> +static int num_nodes;
> +static int num_edges;
> +
> +/*
> + * Graph edge, which connects Graph nodes
> + * and has a weight property.
> + */
> +struct GraphEdge {
> + unsigned int weight;
> + struct GraphNode *node;
> +};
If you move GraphEdge structure bit upward you can take out forward
declaration for this structure above GraphNode.
> +
> +static struct GraphNode *create_graph_node(enum NodeType type, int id)
> +{
> + struct GraphNode *node = 0;
> +
> + if (num_nodes >= MAX_NODES) {
> + igt_info("Too much nodes %d", num_nodes);
> + return NULL;
> + }
> +
> + node = malloc(sizeof(struct GraphNode));
> + node->type = type;
> + node->id = id;
> + node->use_count = 0;
> + node->visited = 0;
> + memset(node->connections, 0,
> + MAX_CONNECTIONS * sizeof(struct GraphEdge *));
You could allocate with calloc instead of malloc + memset + zeroing
individual members.
> + node->num_connections = 0;
> +
> + all_nodes[num_nodes] = node;
> + num_nodes++;
> +
> + return node;
> +}
> +
> +static struct GraphEdge *create_graph_edge(int weight, struct GraphNode *node)
> +{
> + struct GraphEdge *edge = 0;
> +
> + if (num_edges >= MAX_EDGES) {
> + igt_info("Too much edges %d", num_edges);
> + return NULL;
> + }
> +
> + edge = malloc(sizeof(struct GraphEdge));
> +
> + edge->node = node;
> + edge->weight = weight;
> +
> + all_edges[num_edges] = edge;
> + num_edges++;
> +
> + return edge;
> +}
> +
> +static void connect_nodes(struct GraphNode *node1, struct GraphNode *node2, int weight)
> +{
> + struct GraphEdge *edge1;
> + struct GraphEdge *edge2;
> +
> + if (node1->num_connections >= MAX_CONNECTIONS) {
> + igt_info("Node %d has too much connections\n", node1->id);
> + return;
> + }
> +
> + if (node2->num_connections >= MAX_CONNECTIONS) {
> + igt_info("Node %d has too much connections\n", node2->id);
> + return;
> + }
> +
> + edge1 = create_graph_edge(weight, node1);
> + edge2 = create_graph_edge(weight, node2);
> +
> + node1->connections[node1->num_connections] = edge2;
> + node1->num_connections++;
> + node2->connections[node2->num_connections] = edge1;
> + node2->num_connections++;
> +}
> +
> +static void reset_node(struct GraphNode *node)
> +{
> + node->use_count = 0;
> + node->visited = 0;
> + memset(node->connections, 0,
> + MAX_CONNECTIONS * sizeof(struct GraphEdge *));
> + node->num_connections = 0;
> +}
> +
> +static void destroy_node(struct GraphNode *node)
> +{
> + free(node);
> +}
> +
> +static void destroy_edge(struct GraphEdge *edge)
> +{
> + free(edge);
> +}
> +
> +static void destroy_all_edges(void)
> +{
> + int i;
> +
> + for (i = 0; i < num_edges; i++) {
> + destroy_edge(all_edges[i]);
> + }
> + num_edges = 0;
> +}
> +
> +static void destroy_all_nodes(void)
> +{
> + int i;
> +
> + for (i = 0; i < num_nodes; i++) {
> + destroy_node(all_nodes[i]);
> + }
> + num_nodes = 0;
> +}
> +
> +static void reset_all_nodes(void)
> +{
> + int i;
> +
> + destroy_all_edges();
> +
> + for (i = 0; i < num_nodes; i++) {
> + reset_node(all_nodes[i]);
> + }
> +}
> +
> +/*
> + * Traverse and try to print graph in a
> + * somewhat graphical form.
> + */
> +static void traverse_graph(struct GraphNode *start_node,
> + int depth)
> +{
> + int i, space;
> +
> + if (!depth)
> + igt_info("Graph: \n");
> +
> + for (space = 0; space < depth * 4; space++)
> + igt_info(" ");
> +
> + start_node->visited++;
> + igt_info("Type %s id %d\n", node_type_to_str[start_node->type],
> + start_node->id);
> + for (i = 0; i < start_node->num_connections; i++) {
> + if (!start_node->connections[i]->node->visited) {
> + for (space = 0; space < (depth + 1) * 4; space++)
> + igt_info(" ");
> + igt_info("(%d)-> \n", start_node->connections[i]->weight);
> + traverse_graph(start_node->connections[i]->node, depth+2);
> + start_node->connections[i]->node->visited--;
> + }
> + }
> +}
> +
> +bool debug = false;
> +
> +static void print_node_debug(struct GraphNode *node)
> +{
> + igt_info("%s node %d visited %d use count %d\n",
> + node_type_to_str[node->type], node->id,
> + node->visited,
> + node->use_count);
> +}
> +
> +/*
> + * This function recursively searches for a free DBuf slice
> + * for correspondent pipe, based on different constraints, like
> + * weight, usage count. This algorithm is "greedy" which means
> + * that it accounts for local optimum only, which means that
> + * total comparison has to be done in the end.
> + * In graph theory this is called breadth-first search.
> + * As this algorithm tries to utilize nearest neighbours first
> + * it allows to skip many unrequired path calculations which
> + * would be needed if we really would build a matrix like:
> + * Pipe - DBUF
> + * 0 1
> + * 0 1 2
> + *
> + * 1 2 1
> + * Then we would have to go thourgh all N Dbuf * N Pipes * N Pipes!
> + * to check all possible pipe accomodations.
> + * For above matrix the algorithm would immediately return slice 0
> + * for pipe 0 and slice 1 for pipe 1 as nearest neighbours, skipping
> + * unneccessary paths for pipe0 - slice1, pipe1 - slice0.
> + * Anyway for more complex cases, like:
> + * Pipe - DBUF
> + * 0 1
> + * 0 1 1
> + *
> + * 1 2 1
> + * checking for all possible accomodations is still needed. However
> + * nearest "greedy" approach first still allows to filter out some of
> + * the redundant combinations, which would be seen if full matrix is built.
> + * Instead we save time by just choosing the nearest ones first which
> + * are expected to have lower cost(greedy approach), however then
> + * we will still check all possible accomodations as we would do with
> + * full matrix approach.
> + */
> +static struct GraphNode *find_slice_for_pipe(struct GraphNode *start_node,
> + int acc_weight, int max_use_count,
> + unsigned int *total_weight,
> + int depth,
> + int *skip)
> +{
> + int i;
> + struct GraphNode *node = NULL;
> +
> + if (debug) {
> + igt_info("Entering node(resursion depth %d skips %d): \n",
> + depth, *skip);
> + print_node_debug(start_node);
> + igt_info("Accumulated weight %d max use count %d\n",
> + acc_weight, max_use_count);
> + }
> +
> + start_node->visited++;
> +
> + /*
> + * Iterate through neighbouring slices(graph breadth-first search),
> + * checking if it's use count allows usage and skip if needed,
> + * skips are needed when we use multiple slices for
> + * same pipe to prevent algorithm from returning the
> + * same DBuf.
> + */
> + for (i = 0; i < start_node->num_connections; i++) {
> + struct GraphEdge *cur_edge = start_node->connections[i];
> +
> + if (debug) {
> + igt_info(" Neighbour node: ");
> + print_node_debug(cur_edge->node);
> + }
> +
> + if (cur_edge->node->type != DBUF_SLICE)
> + continue;
> +
> + if (cur_edge->node->use_count < max_use_count) {
> + if (!(*skip)) {
> + cur_edge->node->use_count++;
> + *total_weight = acc_weight + cur_edge->weight;
> + node = cur_edge->node;
> + goto out;
> + }
> + }
> +
> + if (*skip)
> + (*skip)--;
> + }
> +
> + /*
> + * If couldn't find suitable DBuf node from our
> + * neighbours, have to continue further, checking
> + * visited flag, to prevent loops.
> + */
> + for (i = 0; i < start_node->num_connections; i++) {
> + struct GraphEdge *cur_edge = start_node->connections[i];
> +
> + if (cur_edge->node->visited)
> + continue;
> +
> + node = find_slice_for_pipe(cur_edge->node,
> + acc_weight + cur_edge->weight,
> + max_use_count, total_weight,
> + depth + 1, skip);
> + cur_edge->node->visited--;
> + if (node)
> + goto out;
> + }
> +
> +out:
> + if (debug)
> + igt_info("Leaving node %d\n", start_node->id);
> +
> + /* If we are at recursion depth 0 - release the root node */
> + if (depth == 0)
> + start_node->visited = 0;
> +
> + return node;
> +}
> +
> +static int hweight32(int mask)
> +{
> + int i;
> + int count = 0;
> +
> + for (i = 0; i < 32; i++)
> + if ((1<<i) & mask)
> + count += 1;
> + return count;
> +}
> +
> +#define I915_MAX_PIPES 4
> +#define I915_MAX_SLICES_PER_PIPE 4
> +#define I915_MAX_SLICES (I915_MAX_PIPES * I915_MAX_SLICES_PER_PIPE)
> +
> +const char *pipe_names[] = { "PIPE_A", "PIPE_B", "PIPE_C", "PIPE_D" };
> +const char *slice_names[] = { "DBUF_S1", "DBUF_S2", "DBUF_S3", "DBUF_S4" };
> +
> +static int factorial(int n)
> +{
> + if (n <= 1)
> + return 1;
> + return n * factorial(n - 1);
> +}
> +
> +static void do_swap(struct GraphNode **pipe_nodes, int m, int n)
> +{
> + struct GraphNode *node1 = pipe_nodes[m];
> + struct GraphNode *node2 = pipe_nodes[n];
> + unsigned long node_val1 = (unsigned long) node1;
> + unsigned long node_val2 = (unsigned long) node2;
> +
> + node_val1 ^= node_val2;
> + node_val2 ^= node_val1;
> + node_val1 ^= node_val2;
> +
> + node1 = (struct GraphNode *)node_val1;
> + node2 = (struct GraphNode *)node_val2;
> +}
> +
> +struct PipeNodesCombination {
> + unsigned int total_weight;
> + struct GraphNode *pipe_nodes[I915_MAX_PIPES];
> + struct GraphNode *slices[I915_MAX_SLICES];
> +};
> +
> +struct IterateContext {
> + struct PipeNodesCombination min_comb;
> + int max_use_count;
> + int num_pipes;
> + int num_slices;
> + int slices_per_pipe;
> + int max_combinations;
> +};
> +
> +struct PipeDBuf {
> + struct GraphNode *pipe;
> + struct GraphNode *dbuf[I915_MAX_SLICES_PER_PIPE];
> +};
> +
> +static void print_pipe_nodes(struct GraphNode **pipe_nodes)
> +{
> + int i;
> + for (i = 0; i < I915_MAX_PIPES; i++) {
> + if (!pipe_nodes[i])
> + igt_info("(null)");
> + else
> + igt_info("%d", pipe_nodes[i]->id);
> + if ( i < (I915_MAX_PIPES - 1))
> + igt_info(" ,");
> + }
> + igt_info("\n");
> +}
> +
> +
> +static void compare_weight(struct IterateContext *ctx,
> + struct PipeNodesCombination *comb)
> +{
> + unsigned int weight;
> + int j = 0;
> + int pipe = 0;
> + int skip = 0;
> + int slice_num = 0;
> +
> + comb->total_weight = 0;
> +
> + if (debug) {
> + igt_info("Combination: ");
> + print_pipe_nodes(comb->pipe_nodes);
> + }
> +
> + /*
> + * Get a free DBuf slice for each pipe, then
> + * calculate total weight, which will be
> + * then compared to minimal weight to find
> + * combination with total minimal weight.
> + */
> + while (pipe < I915_MAX_PIPES) {
> + if (comb->pipe_nodes[pipe]) {
> + slice_num = 0;
> + skip = 0;
> + while (slice_num < ctx->slices_per_pipe) {
> + skip = slice_num;
> + comb->slices[j] =
> + find_slice_for_pipe(comb->pipe_nodes[pipe],
> + 0, ctx->max_use_count,
> + &weight, 0, &skip);
> + if (!comb->slices[j])
> + break;
> + slice_num++;
> + j++;
> + comb->total_weight += weight;
> + }
> + } else {
> + j += ctx->slices_per_pipe;
> + }
> + pipe++;
> + }
> +
> + if (debug)
> + igt_info("Total weight %d\n", comb->total_weight);
> +
> + for (j = 0; j < I915_MAX_SLICES; j++) {
> + if (comb->slices[j])
> + comb->slices[j]->use_count = 0;
> + }
> +
> + if (ctx->min_comb.total_weight > comb->total_weight) {
> + memcpy(&ctx->min_comb, comb,
> + sizeof(struct PipeNodesCombination));
> + }
> +}
> +
> +/*
> + * Generate all possible combinations for elements in
> + * pipe array(total is calculated as n!).
> + */
> +static void iterate_all_combinations(struct IterateContext *ctx,
> + struct GraphNode **pipe_nodes,
> + int start, int step,
> + void (*func)(struct IterateContext *ctx,
> + struct PipeNodesCombination *comb))
> +{
> + int i, j;
> + struct GraphNode *nodes[I915_MAX_PIPES];
> + struct PipeNodesCombination comb;
> +
> + memcpy(comb.pipe_nodes, pipe_nodes,
> + I915_MAX_PIPES * sizeof(struct GraphNode *));
> + memset(comb.slices, 0,
> + I915_MAX_SLICES * sizeof(struct GraphNode *));
> +
> + if (func)
> + (*func)(ctx, &comb);
> +
> + if ((step == 0) || (ctx->num_pipes == 1))
> + return;
> +
> + for (j = step; j > 0; j--) {
> + for (i = start; i < I915_MAX_PIPES - j; i++) {
> + if (!pipe_nodes[i] || !pipe_nodes[i + j])
> + continue;
> + memcpy(nodes, pipe_nodes,
> + I915_MAX_PIPES * sizeof(struct GraphNode *));
> + do_swap(nodes, i, i + j);
> + iterate_all_combinations(ctx, nodes, i + 1,
> + step - 1, func);
> + }
> + }
> +}
> +
> +/*
> + * This function calculates and prints optimal
> + * weight-based DBuf assignment for active pipes.
> + */
> +static int
> +print_dbuf_mask_for_pipes(int active_pipes,
> + struct GraphNode **pipe_nodes,
> + int num_slices)
> +{
> + int i;
> + int num_pipes = hweight32(active_pipes), pipes_count;
> + int max_use_count = num_slices < num_pipes ? 2 : 1;
> + int pipe = 0;
> + int slices_per_pipe = max(num_slices / num_pipes, 1);
> + struct IterateContext ctx;
> + struct PipeDBuf pipe_dbuf[I915_MAX_PIPES];
> +
> + memcpy(ctx.min_comb.pipe_nodes, pipe_nodes,
> + sizeof(struct GraphNode *) * I915_MAX_PIPES);
> + memset(ctx.min_comb.slices, 0,
> + I915_MAX_SLICES * sizeof(struct GraphNode *));
> + ctx.min_comb.total_weight = ~0;
> + ctx.max_use_count = max_use_count;
> + ctx.num_pipes = num_pipes;
> + ctx.num_slices = num_slices;
> + ctx.slices_per_pipe = slices_per_pipe;
> + ctx.max_combinations = factorial(num_pipes);
> +
> + iterate_all_combinations(&ctx, pipe_nodes,
> + 0, I915_MAX_PIPES - 1, compare_weight);
> +
> + if (debug)
> + igt_info("Combination with least weight %d(total combinations %d):\n",
> + ctx.min_comb.total_weight, ctx.max_combinations);
> +
> + memset(pipe_dbuf, 0, sizeof(struct PipeDBuf) * I915_MAX_PIPES);
> +
> + for (i = 0; i < I915_MAX_PIPES; i++) {
> + if (ctx.min_comb.pipe_nodes[i]) {
> + int pipe_index = ctx.min_comb.pipe_nodes[i]->id;
> +
> + pipe_dbuf[pipe_index].pipe = ctx.min_comb.pipe_nodes[i];
> + memcpy(&pipe_dbuf[pipe_index].dbuf,
> + &ctx.min_comb.slices[i * slices_per_pipe],
> + sizeof(struct GraphNode *) * slices_per_pipe);
> + }
> + }
> +
> + igt_info("\t\t{\n");
> + pipe = 0;
> + igt_info("\t\t\t\t.active_pipes = ");
> + for (i = 0; i < I915_MAX_PIPES; i++) {
> + if (pipe_dbuf[i].pipe) {
> + igt_info("BIT(%s)", pipe_names[pipe_dbuf[i].pipe->id]);
> + if (pipe < (num_pipes - 1))
> + igt_info(" | ");
> + pipe++;
> + }
> + }
> + igt_info(",\n\t\t\t\t{\n");
> + num_pipes = 0;
> + for (pipe = 0; pipe < I915_MAX_PIPES; pipe++)
> + if (pipe_dbuf[pipe].pipe)
> + num_pipes++;
> +
> + pipes_count = 0;
> + for (pipe = 0; pipe < I915_MAX_PIPES; pipe++) {
> + if (pipe_dbuf[pipe].pipe) {
> + igt_info("\t\t\t\t\t\t[%s] = ", pipe_names[pipe_dbuf[pipe].pipe->id]);
> + for (i = 0; i < slices_per_pipe; i++) {
> + int slice_index_converted;
> + struct GraphNode *next_slice =
> + pipe_dbuf[pipe].dbuf[i+1];
> + bool not_last = i < (slices_per_pipe - 1);
> +
> + if (!pipe_dbuf[pipe].dbuf[i])
> + break;
> +
> + slice_index_converted =
> + pipe_dbuf[pipe].dbuf[i]->id;
> +
> + if (not_last && next_slice) {
> + igt_info("BIT(DBUF_S%d) | ",
> + slice_index_converted + 1);
> + } else {
> + igt_info("BIT(DBUF_S%d)",
> + slice_index_converted + 1);
> + }
> + pipes_count++;
> + }
> + if (pipes_count < num_pipes) {
> + igt_info(",\n");
> + }
> + else
> + break;
> + }
> + }
> + igt_info("\n\t\t\t\t}\n\t\t},\n");
> +
> + return 0;
> +}
> +
> +static void print_table(struct GraphNode **pipes, int num_pipes,
> + struct GraphNode **slices, int num_slices,
> + int slices_per_pipe)
> +{
> + unsigned int i, j;
> + int num_combinations = 1 << num_pipes;
> +
> + if (num_pipes > I915_MAX_PIPES) {
> + igt_info("Too many pipes %d\n", num_pipes);
> + return;
> + }
> +
> + if (num_slices > I915_MAX_SLICES) {
> + igt_info("Too many slices %d\n", num_slices);
> + return;
> + }
> +
> + igt_info("/* Autogenerated with igt/tools/intel_dbuf_map tool: */\n");
> + igt_info("{\n");
> + for (i = 1; i < num_combinations; i++) {
> + struct GraphNode *tmp[I915_MAX_PIPES];
> +
> + memcpy(tmp, pipes, I915_MAX_PIPES * sizeof(struct GraphNode *));
> +
> + for (j = 0; j < I915_MAX_SLICES; j++) {
> + if (slices[j])
> + slices[j]->use_count = 0;
> + }
> +
> + for (j = 0; j < I915_MAX_PIPES; j++) {
> + if (!(i & (1 << j))) {
> + tmp[j] = 0;
> + }
> + }
> +
> + if (print_dbuf_mask_for_pipes(i, tmp, num_slices))
> + break;
> + }
> + igt_info("};\n");
> +}
> +
> +enum opt {
> + OPT_UNKNOWN = '?',
> + OPT_END = -1,
> + OPT_GEN,
> + OPT_USAGE,
> + OPT_GRAPH,
> + OPT_DEBUG,
> + OPT_SLICES_PER_PIPE
> +};
> +
> +static void usage(const char *toolname)
> +{
> + fprintf(stderr, "usage: %s", toolname);
> + fprintf(stderr, " [--gen=<GEN>]"
> + " [--slices_per_pipe=<number>]"
> + " [--graph=\"PIPE_A-DBUF_S1-PIPE_B-...\"]"
> + " [--debug]"
> + " [--help]\n");
> +}
> +
> +static int pipe_str_to_num(const char *str)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(pipe_names); i++) {
> + if (!strcmp(str, pipe_names[i])) {
> + return i;
> + }
> + }
> + return -1;
> +}
> +
> +static int dbuf_str_to_num(const char *str)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(slice_names); i++) {
> + if (!strcmp(str, slice_names[i])) {
> + return i;
> + }
> + }
> + return -1;
> +}
> +
> +#define MAX_GRAPH_STR 80
> +
> +static void parse_graph_str(char *graph_str, struct GraphNode **pipes,
> + struct GraphNode **slices, int *num_slices,
> + int *num_pipes)
> +{
> + char* token = strtok(graph_str, "-");
> + struct GraphNode *cur_node = NULL, *last_node = NULL;
> + int n_pipes = 0, n_slices = 0;
> +
> + while (token != NULL) {
> + int index;
> +
> + index = pipe_str_to_num(token);
> +
> + last_node = cur_node;
> + if (index >= 0) {
> + cur_node = pipes[index];
> + n_pipes++;
> + } else {
> + index = dbuf_str_to_num(token);
> + if (index >= 0) {
> + cur_node = slices[index];
> + n_slices++;
> + }
> + }
> + if (last_node && cur_node)
> + connect_nodes(last_node, cur_node, 1);
> +
> + token = strtok(NULL, "-");
> + }
> + *num_slices = n_slices;
> + *num_pipes = n_pipes;
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct GraphNode *pipes[I915_MAX_PIPES];
> + struct GraphNode *slices[I915_MAX_SLICES];
> + struct GraphNode *pipeA, *pipeB, *pipeC, *pipeD;
> + struct GraphNode *slice1, *slice2, *slice3, *slice4;
> + int gen = 12, index;
> + enum opt opt;
> + char *endp;
> + char graph_str[MAX_GRAPH_STR];
> + int slices_per_pipe = I915_MAX_SLICES_PER_PIPE;
> + const char *toolname = argv[0];
> + static struct option options[] = {
> + { "gen", required_argument, NULL, OPT_GEN },
> + { "graph", required_argument, NULL, OPT_GRAPH },
> + { "slices_per_pipe", required_argument, NULL, OPT_SLICES_PER_PIPE },
> + { "help", no_argument, NULL, OPT_USAGE },
> + { "debug", no_argument, NULL, OPT_DEBUG },
> + { 0 }
> + };
> +
> + num_edges = 0;
> + num_nodes = 0;
> +
> + for (opt = 0; opt != OPT_END; ) {
> + opt = getopt_long(argc, argv, "", options, &index);
> +
> + switch (opt) {
> + case OPT_GEN:
> + gen = strtoul(optarg, &endp, 0);
> + break;
> + case OPT_SLICES_PER_PIPE:
> + slices_per_pipe = min(strtoul(optarg, &endp, 0),
> + I915_MAX_SLICES_PER_PIPE);
> + break;
> + case OPT_DEBUG:
> + debug = true;
> + break;
> + case OPT_GRAPH:
> + strncpy(graph_str, optarg, MAX_GRAPH_STR);
> + graph_str[MAX_GRAPH_STR - 1] = 0;
> + gen = 0;
> + case OPT_END:
> + break;
> + case OPT_USAGE: /* fall-through */
> + case OPT_UNKNOWN:
> + usage(toolname);
> + return EXIT_FAILURE;
> + }
> + }
> + pipeA = create_graph_node(PIPE, 0);
> + pipeB = create_graph_node(PIPE, 1);
> + pipeC = create_graph_node(PIPE, 2);
> + pipeD = create_graph_node(PIPE, 3);
> + slice1 = create_graph_node(DBUF_SLICE, 0);
> + slice2 = create_graph_node(DBUF_SLICE, 1);
> + slice3 = create_graph_node(DBUF_SLICE, 2);
> + slice4 = create_graph_node(DBUF_SLICE, 3);
> +
> + memset(pipes, 0, I915_MAX_PIPES * sizeof(struct GraphNode *));
> + memset(slices, 0, I915_MAX_SLICES * sizeof(struct GraphNode *));
> +
> + if (!gen) {
> + int num_slices = 0, num_pipes = 0;
> + /*
> + * Prepare to generate assignments for ICL, which
> + * has 3 pipes and 2 DBuf slices.
> + */
cut'n'paste comment above? :)
> + slices[0] = slice1;
> + slices[1] = slice2;
> + slices[2] = slice3;
> + slices[3] = slice4;
> + pipes[0] = pipeA;
> + pipes[1] = pipeB;
> + pipes[2] = pipeC;
> + pipes[3] = pipeD;
> +
> + parse_graph_str(graph_str, pipes, slices, &num_slices,
> + &num_pipes);
> +
> + traverse_graph(pipes[0], 0);
> +
> + print_table(pipes, num_pipes, slices, num_slices, slices_per_pipe);
> +
> + } else if (gen == 11) {
> +
> + /*
> + * Prepare to generate assignments for ICL, which
> + * has 3 pipes and 2 DBuf slices.
> + */
> + slices[0] = slice1;
> + slices[1] = slice2;
> + pipes[0] = pipeA;
> + pipes[1] = pipeB;
> + pipes[2] = pipeC;
> +
> + /*
> + * BSpec 12716. Generate DBuf Slices table for ICL,
> + * Graph(each edge assumed to have weight 1):
> + * PipeA - DBUF_S1 - PipeB - PipeC - DBUF_S2
> + */
> + connect_nodes(pipeA, slice1, 1);
> + connect_nodes(slice1, pipeB, 1);
> + connect_nodes(pipeB, pipeC, 1);
> + connect_nodes(pipeC, slice2, 1);
> +
> + traverse_graph(pipeA, 0);
> +
> + print_table(pipes, 3, slices, 2, 2);
> +
> + } else if (gen == 12) {
> +
> + /*
> + * Prepare to generate assignments for TGL, which
> + * has 4 pipes and 2 DBuf slices.
> + */
> + slices[0] = slice1;
> + slices[1] = slice2;
> + pipes[0] = pipeA;
> + pipes[1] = pipeB;
> + pipes[2] = pipeC;
> + pipes[3] = pipeD;
> +
> + /*
> + * BSpec 49255. Generate DBuf Slices table for TGL.
> + * Graph(each edge assumed to have weight 1):
> + * PipeD - DBUF_S2 - PipeC - PipeA - DBUF_S1 - PipeB
> + */
> + connect_nodes(pipeD, slice2, 1);
> + connect_nodes(slice2, pipeC, 1);
> + connect_nodes(pipeC, pipeA, 1);
> + connect_nodes(pipeA, slice1, 1);
> + connect_nodes(slice1, pipeB, 1);
> +
> + traverse_graph(pipeA, 0);
> +
> + print_table(pipes, 4, slices, 2, 2);
> + }
What about if gen == 13? Maybe add some form of comment if none of the
paths was chosen. Convert to switch {case:; default:;} where default
would list why no path was chosen?
> +
> + reset_all_nodes();
> +
> + /*
> + * Further platforms can generate table same way.
> + */
> +
> + destroy_all_nodes();
> +
> + return 0;
> +}
> +
> diff --git a/tools/meson.build b/tools/meson.build
> index eecb122b..2b8df4b4 100644
> --- a/tools/meson.build
> +++ b/tools/meson.build
> @@ -35,6 +35,7 @@ tools_progs = [
> 'intel_watermark',
> 'intel_gem_info',
> 'intel_gvtg_test',
> + 'intel_dbuf_map',
> 'dpcd_reg',
> ]
> tool_deps = igt_deps
>
---------------------------------------------------------------------
Intel Finland Oy
Registered Address: PL 281, 00181 Helsinki
Business Identity Code: 0357606 - 4
Domiciled in Helsinki
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
More information about the igt-dev
mailing list