Pass 'interval' value to date_trunc function in PostgreSQL procedure
I am not entirely sure about your objective, but what you show works with legal identifiers. "end" is a reserved word and I wouldn't use "start" either. Reserved words require double-quoting, best avoided to begin with:

DO
$$
DECLARE
   ts_start       timestamp := '2019-03-20 00:00:00';  -- legal name
   ts_end         timestamp := '2019-07-02 01:12:13';
   i              numeric   := (select to_char(age(ts_end, ts_start),'MM'));
   interval_value varchar   := i || ' month'; 
   xyz            date      := (select date_trunc('month',ts_start::date)
                                     + interval_value::text::interval);
BEGIN
   RAISE NOTICE '%', xyz;
END
$$;


Can be much simpler, though:

DO
$$
DECLARE
   ts_start       timestamp := '2019-03-20 00:00:00';
   ts_end         timestamp := '2019-06-29 01:12:13';
   xyz            date      := date_trunc('month', ts_start)
                             + date_trunc('month', age(ts_end, ts_start));
BEGIN
   RAISE NOTICE '%', xyz;
END
$$;


db&lt;>fiddle here (raising exceptions to display the result)